Implement cat-like filtering behaviour for encrypt/decrypt

This allows the following invocations:

    # Interactive use, like gpg
    ansible-vault encrypt --output x

    # Non-interactive, for scripting
    echo plaintext|ansible-vault encrypt --output x

    # Separate input and output files
    ansible-vault encrypt input.yml --output output.yml

    # Existing usage (in-place encryption) unchanged
    ansible-vault encrypt inout.yml

…and the analogous cases for ansible-vault decrypt as well.

In all cases, the input and output files can be '-' to read from stdin
or write to stdout. This permits sensitive data to be encrypted and
decrypted without ever hitting disk.
pull/12112/head
Abhijit Menon-Sen 9 years ago
parent 32b38d4e29
commit e7eebb6954

@ -262,6 +262,8 @@ class CLI(object):
parser.add_option('--new-vault-password-file',
dest='new_vault_password_file', help="new vault password file for rekey", action="callback",
callback=CLI.expand_tilde, type=str)
parser.add_option('--output', default=None, dest='output_file',
help='output file name for encrypt or decrypt; use - for stdout')
if subset_opts:

@ -63,7 +63,19 @@ class VaultCLI(CLI):
self.options, self.args = self.parser.parse_args()
self.display.verbosity = self.options.verbosity
if len(self.args) == 0:
if self.options.output_file:
if self.action not in ['encrypt','decrypt']:
raise AnsibleOptionsError("The --output option can be used only with ansible-vault encrypt/decrypt")
# This restriction should remain in place until it's possible to
# load multiple YAML records from a single file, or it's too easy
# to create an encrypted file that can't be read back in. But in
# the meanwhile, "cat a b c|ansible-vault encrypt --output x" is
# a workaround.
if len(self.args) > 1:
raise AnsibleOptionsError("At most one input file may be used with the --output option")
elif len(self.args) == 0:
raise AnsibleOptionsError("Vault requires at least one filename as a parameter")
def run(self):
@ -87,20 +99,27 @@ class VaultCLI(CLI):
self.execute()
def execute_create(self):
def execute_encrypt(self):
if len(self.args) > 1:
raise AnsibleOptionsError("ansible-vault create can take only one filename argument")
for f in self.args or ['-']:
self.editor.encrypt_file(f, output_file=self.options.output_file)
self.editor.create_file(self.args[0])
self.display.display("Encryption successful", stderr=True)
def execute_decrypt(self):
for f in self.args:
self.editor.decrypt_file(f)
for f in self.args or ['-']:
self.editor.decrypt_file(f, output_file=self.options.output_file)
self.display.display("Decryption successful", stderr=True)
def execute_create(self):
if len(self.args) > 1:
raise AnsibleOptionsError("ansible-vault create can take only one filename argument")
self.editor.create_file(self.args[0])
def execute_edit(self):
for f in self.args:
self.editor.edit_file(f)
@ -110,13 +129,6 @@ class VaultCLI(CLI):
for f in self.args:
self.editor.view_file(f)
def execute_encrypt(self):
for f in self.args:
self.editor.encrypt_file(f)
self.display.display("Encryption successful", stderr=True)
def execute_rekey(self):
for f in self.args:
if not (os.path.isfile(f)):

@ -20,6 +20,7 @@ __metaclass__ = type
import os
import shlex
import shutil
import sys
import tempfile
from io import BytesIO
from subprocess import call
@ -258,21 +259,21 @@ class VaultEditor:
# and restore umask
os.umask(old_umask)
def encrypt_file(self, filename):
def encrypt_file(self, filename, output_file=None):
check_prereqs()
plaintext = self.read_data(filename)
ciphertext = self.vault.encrypt(plaintext)
self.write_data(ciphertext, filename)
self.write_data(ciphertext, output_file or filename)
def decrypt_file(self, filename):
def decrypt_file(self, filename, output_file=None):
check_prereqs()
ciphertext = self.read_data(filename)
plaintext = self.vault.decrypt(ciphertext)
self.write_data(plaintext, filename)
self.write_data(plaintext, output_file or filename)
def create_file(self, filename):
""" create a new encrypted file """
@ -327,7 +328,10 @@ class VaultEditor:
def read_data(self, filename):
try:
f = open(filename, "rb")
if filename == '-':
f = sys.stdin
else:
f = open(filename, "rb")
data = f.read()
f.close()
except Exception as e:
@ -336,9 +340,12 @@ class VaultEditor:
return data
def write_data(self, data, filename):
if os.path.isfile(filename):
os.remove(filename)
f = open(filename, "wb")
if filename == '-':
f = sys.stdout
else:
if os.path.isfile(filename):
os.remove(filename)
f = open(filename, "wb")
f.write(to_bytes(data, errors='strict'))
f.close()

Loading…
Cancel
Save