Added cleartext option to passphrase generation

Added a force-cleartext option (false by default) which forces
passphrases to be generated in cleartext rather than encrypted.

Change-Id: I157a40103f67f85a24976b4f59aa46f2d4b92334
This commit is contained in:
Lev Morgan 2019-03-20 16:45:45 -05:00 committed by Alexander Hughes
parent 820df6d625
commit 52b61b8cfd
6 changed files with 54 additions and 25 deletions

View File

@ -874,6 +874,15 @@ are placed in the following folder structure under ``save_location``:
<save_location>/site/<site_name>/secrets/passphrases/<passphrase_name.yaml>
**-i / --interactive** (Optional). False by default.
Generate passphrases interactively, not automatically.
**--force-cleartext** (Optional). False by default.
Force cleartext generation of passphrases. This is not
recommended.
Usage:
::

View File

@ -106,7 +106,7 @@ ALLOW_MISSING_SUBSTITUTIONS_OPTION = click.option(
type=click.BOOL,
default=True,
show_default=True,
help="Raise Deckhand exception on missing substition sources.")
help='Raise Deckhand exception on missing substition sources.')
EXCLUDE_LINT_OPTION = click.option(
'-x',
@ -131,7 +131,7 @@ SITE_REPOSITORY_ARGUMENT = click.argument(
@click.option(
'-v',
'--verbose',
is_flag=bool,
is_flag=True,
default=False,
help='Enable debug logging')
def main(*, verbose):
@ -235,7 +235,7 @@ def site(*, site_repository, clone_path, extra_repositories, repo_key,
help='Directory to output the complete site definition. Created '
'automatically if it does not already exist.')
@click.option(
'--validate',
'--validate/--no-validate',
'validate',
is_flag=True,
# TODO(felipemonteiro): Potentially set this to True in the future. This
@ -479,7 +479,7 @@ def generate_pki(site_name, author, days):
@click.option(
'-f',
'--filename',
'file_name',
'filename',
help='The relative file path for the file to be wrapped.')
@click.option(
'-o',
@ -512,7 +512,7 @@ def generate_pki(site_name, author, days):
show_default=True,
help='Whether to encrypt the wrapped file.')
@click.argument('site_name')
def wrap_secret_cli(*, site_name, author, file_name, output_path, schema,
def wrap_secret_cli(*, site_name, author, filename, output_path, schema,
name, layer, encrypt):
"""Wrap a bare secrets file in a YAML and ManagedDocument.
@ -520,7 +520,7 @@ def wrap_secret_cli(*, site_name, author, file_name, output_path, schema,
engine.repository.process_repositories(site_name,
overwrite_existing=True)
wrap_secret(author, file_name, output_path, schema,
wrap_secret(author, filename, output_path, schema,
name, layer, encrypt)
@ -652,13 +652,21 @@ def generate():
'-i',
'--interactive',
'interactive',
is_flag=bool,
is_flag=True,
default=False,
help='Generate passphrases interactively, not automatically')
def generate_passphrases(*, site_name, save_location, author, interactive):
@click.option(
'--force-cleartext',
'force_cleartext',
is_flag=True,
default=False,
show_default=True,
help='Force cleartext generation of passphrases. This is not recommended.')
def generate_passphrases(*, site_name, save_location, author, interactive,
force_cleartext):
engine.repository.process_repositories(site_name)
engine.secrets.generate_passphrases(site_name, save_location, author,
interactive)
engine.secrets.generate_passphrases(
site_name, save_location, author, interactive, force_cleartext)
@secrets.command(
@ -736,7 +744,7 @@ def decrypt(*, path, save_location, overwrite, site_name):
os.chmod(file_save_location, 0o600)
@main.group(help="Miscellaneous generate commands")
@main.group(help='Miscellaneous generate commands')
def generate():
pass
@ -753,7 +761,7 @@ def generate():
help='Generate a passphrase of the given length. '
'Length is >= 24, no maximum length.')
def generate_passphrase(length):
click.echo("Generated Passhprase: {}".format(
click.echo('Generated Passhprase: {}'.format(
engine.secrets.generate_crypto_string(length)))

View File

@ -52,7 +52,7 @@ class PassphraseGenerator(BaseGenerator):
self._sitename, documents=self._documents)
self._pass_util = CryptoString()
def generate(self, interactive=False):
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
@ -60,6 +60,9 @@ class PassphraseGenerator(BaseGenerator):
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, run interactively
:param bool force_cleartext: If true, don't encrypt
"""
for p_name in self._catalog.get_passphrase_names:
passphrase = None
@ -76,7 +79,13 @@ class PassphraseGenerator(BaseGenerator):
passphrase = passphrase.encode()
passphrase = base64.b64encode(passphrase)
docs = list()
storage_policy = self._catalog.get_storage_policy(p_name)
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,

View File

@ -131,21 +131,24 @@ def _get_dest_path(repo_base, file_path, save_location):
return file_path
def generate_passphrases(site_name, save_location, author, interactive=False):
def generate_passphrases(site_name, save_location, author, interactive=False,
force_cleartext=False):
"""
Look for the site passphrase catalogs, and for every passphrase entry in
the passphrase catalog generate a passphrase document, wrap the
passphrase document in a pegleg managed document, and encrypt the
passphrase data.
:param interactive: Whether to generate the results interactively
:param str site_name: The site to read from
:param str save_location: Location to write files to
:param str author:
:param str author: Author who's generating the files
:param bool interactive: Whether to generate the results interactively
:param bool force_cleartext: Whether to generate results in clear text
"""
PassphraseGenerator(site_name, save_location, author).generate(
interactive=interactive)
PassphraseGenerator(
site_name, save_location, author).generate(
interactive=interactive, force_cleartext=force_cleartext)
def generate_crypto_string(length):
@ -159,12 +162,12 @@ def generate_crypto_string(length):
return CryptoString().get_crypto_string(length)
def wrap_secret(author, file_name, output_path, schema,
def wrap_secret(author, filename, output_path, schema,
name, layer, encrypt):
"""Wrap a bare secrets file in a YAML and ManagedDocument.
:param author: author for ManagedDocument
:param file_name: file path for input file
:param filename: file path for input file
:param output_path: file path for output file
:param schema: schema for wrapped document
:param name: name for wrapped document
@ -173,9 +176,9 @@ def wrap_secret(author, file_name, output_path, schema,
"""
if not output_path:
output_path = os.path.splitext(file_name)[0] + ".yaml"
output_path = os.path.splitext(filename)[0] + ".yaml"
with open(file_name, "r") as in_fi:
with open(filename, "r") as in_fi:
data = in_fi.read()
inner_doc = {

View File

@ -75,7 +75,7 @@ def site_files(site_name):
def site_files_by_repo(site_name):
"""Yield tuples of repo_base, file_name."""
"""Yield tuples of repo_base, filename."""
params = load_as_params(site_name)
dir_map = files.directories_for_each_repo(**params)
for repo, dl in dir_map.items():

View File

@ -411,7 +411,7 @@ def file_in_subdir(filename, _dir):
:return: Whether _dir is a parent of the file
:rtype: bool
"""
file_path, file_name = os.path.split(
file_path, filename = os.path.split(
os.path.realpath(filename))
return _dir in file_path.split(os.path.sep)