Allow decryption of directories

This change allows users to specify a directory or file to be decrypted.

Allows directory decryption.

Adds flag to overwrite encrypted file with decrypted data.

Intelligently recognizes paths vs files in CLI input and outputs data 
accordingly.

Change-Id: I0d5e77f0eb1adb42165aa9b214aa90a0db0a3131
This commit is contained in:
Ian H. Pittwood 2019-05-06 11:30:59 -05:00 committed by Ian Pittwood
parent 5b8bfd9211
commit 2fa6a1a7bd
5 changed files with 72 additions and 38 deletions

View File

@ -662,9 +662,9 @@ Example without optional save location:
Decrypt
^^^^^^^
Unwrap an encrypted secrets document from a `Pegleg Managed Documents`_,
decrypt the encrypted secrets, and dump the cleartext secrets file to
``stdout``.
Unwrap one or more encrypted secrets document from
`Pegleg Managed Documents`_, decrypt the encrypted secrets, and dump the
cleartext to stdout or a specified location.
**site_name** (Required).
@ -673,21 +673,25 @@ repository folder structure. This is used to ensure the correct revision of
the site and global repositories are used, as specified in the site's
:file:`site-definition.yaml`.
**-f / filename** (Required).
**-p / --path** (Required).
The absolute path to the pegleg managed encrypted secrets file.
Path to pegleg managed encrypted secrets file or directory of files.
**-s / --save-location** (Optional).
**-s / save-location** (Optional).
The desired output path for the decrypted file. If not specified, decrypted
data will output to stdout.
The desired output path for the decrypted file. If not specified, it will be
printed to stdout.
**-o / --overwrite** (Optional). False by default.
When set, encrypted file(s) at the specified path will be overwritten with
the decrypted data. Overrides ``--save-location`` option.
Usage:
::
./pegleg.sh site <options> secrets decrypt <site_name> -f <file_path>
./pegleg.sh site <options> secrets decrypt <site_name> -p <path>
[-s <output_path>]
Examples

View File

@ -679,27 +679,45 @@ def encrypt(*, save_location, author, site_name):
help='Command to unwrap and decrypt one site '
'secrets document and print it to stdout.')
@click.option(
'-f',
'--filename',
'file_name',
help='The file to decrypt')
'-p',
'--path',
'path',
type=click.Path(exists=True, readable=True),
required=True,
help='The file or directory path to decrypt.')
@click.option(
'-s',
'--save-location',
'save_location',
default=None,
help='The destination where the decrypted file should be saved. '
'If not specified, it will be printed to stdout.')
help='The destination where the decrypted file(s) should be saved. '
'If not specified, decrypted data will output to stdout.')
@click.option(
'-o',
'--overwrite',
'overwrite',
is_flag=True,
default=False,
help='Overwrites original file(s) at path with decrypted data when set. '
'Overrides --save-location option.')
@click.argument('site_name')
def decrypt(*, file_name, save_location, site_name):
def decrypt(*, path, save_location, overwrite, site_name):
engine.repository.process_repositories(site_name)
decrypted = engine.secrets.decrypt(file_name)
if save_location is None:
click.echo(decrypted)
decrypted = engine.secrets.decrypt(path)
if overwrite:
for key, value in decrypted.items():
files.write(key, value)
os.chmod(key, 0o600)
elif save_location is None:
for value in decrypted.values():
click.echo(value)
else:
files.write(save_location, decrypted)
os.chmod(save_location, 0o600)
for key, value in decrypted.items():
file_name = os.path.split(key)
file_save_location = os.path.join(save_location, file_name)
files.write(file_save_location, value)
os.chmod(file_save_location, 0o600)
@main.group(help="Miscellaneous generate commands")

View File

@ -12,11 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from glob import glob
import logging
import os
import yaml
from prettytable import PrettyTable
import yaml
from pegleg.engine.catalog.pki_utility import PKIUtility
from pegleg.engine.generators.passphrase_generator import PassphraseGenerator
@ -68,7 +69,7 @@ def encrypt(save_location, author, site_name):
'No secret documents were found for site: {}'.format(site_name))
def decrypt(file_path):
def decrypt(path):
"""Decrypt one secrets file, and print the decrypted file to standard out.
Search the specified file_path for a file.
@ -77,17 +78,31 @@ def decrypt(file_path):
If the file is found, but it is not encrypted, print the contents of the
file to standard out.
Passphrase and salt for the decryption are read from environment variables.
:param file_path: Path to the file to be unwrapped and decrypted.
:type file_path: string
:param path: Path to the file to be unwrapped and decrypted.
:type path: string
:return: The decrypted secrets
:rtype: list
:rtype: dict
"""
LOG.info('Started decrypting...')
if os.path.isfile(file_path):
return PeglegSecretManagement(file_path).decrypt_secrets()
file_dict = {}
if not os.path.exists(path):
LOG.error('Path: {} was not found. Check your path and site name, '
'and try again.'.format(path))
return file_dict
if os.path.isfile(path):
file_dict[path] = PeglegSecretManagement(path).decrypt_secrets()
else:
LOG.info('File: {} was not found. Check your file path and name, '
'and try again.'.format(file_path))
match = os.path.join(path, '**', '*.yaml')
file_list = glob(match, recursive=True)
if not file_list:
LOG.warning('No YAML files were discovered in path: {}'
.format(path))
for file_path in file_list:
file_dict[file_path] = PeglegSecretManagement(
file_path).decrypt_secrets()
return file_dict
def _get_dest_path(repo_base, file_path, save_location):

View File

@ -113,11 +113,10 @@ data: {0}-password
encrypted_files = listdir(save_location_str)
assert len(encrypted_files) > 0
# for _file in encrypted_files:
decrypted = secrets.decrypt(str(save_location.join(
"site/cicd/secrets/passphrases/"
"cicd-passphrase-encrypted.yaml")))
assert yaml.load(decrypted) == yaml.load(passphrase_doc)
encrypted_path = str(save_location.join("site/cicd/secrets/passphrases/"
"cicd-passphrase-encrypted.yaml"))
decrypted = secrets.decrypt(encrypted_path)
assert yaml.load(decrypted[encrypted_path]) == yaml.load(passphrase_doc)
def test_pegleg_secret_management_constructor():

View File

@ -555,9 +555,7 @@ class TestSiteSecretsActions(BaseCLIActionTest):
assert "encrypted" in ceph_fsid["data"]
assert "managedDocument" in ceph_fsid["data"]
relative_file_path = os.path.join("secrets", "passphrases",
"ceph_fsid.yaml")
secrets_opts = ['secrets', 'decrypt', '-f', relative_file_path,
secrets_opts = ['secrets', 'decrypt', '-p', file_path,
self.site_name]
result = self.runner.invoke(cli.site, ['-r', repo_path] + secrets_opts)
assert result.exit_code == 0, result.output