1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00

Merge pull request #105 from mozilla/add-insert-flag

add --set flag
This commit is contained in:
Julien Vehent [:ulfr]
2016-09-08 10:42:22 -04:00
committed by GitHub
4 changed files with 111 additions and 60 deletions

View File

@@ -596,6 +596,31 @@ them.
$ sops -d ~/git/svc/sops/example.yaml -t '["an_array"][1]'
secretuser2
Set a sub-part in a document tree
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`sops` can set a specific part of a YAML or JSON document, by providing
the path and value in the `--set` command line flag. This is useful to
set specific values, like keys, without needing an editor.
.. code:: bash
$ sops ~/git/svc/sops/example.yaml --set '["app2"]["key"]' '"app2keystringvalue"'
The tree path syntax uses regular python dictionary syntax, without the
variable name. Set to keys by naming them, and array elements by
numbering them.
.. code:: bash
$ sops ~/git/svc/sops/example.yaml --set '["an_array"][1]' '"secretuser2"'
The value must be formatted as json.
.. code:: bash
$ sops ~/git/svc/sops/example.yaml --set '["an_array"][1]' '{"uid1":null,"uid2":1000,"uid3":["bob"]}'
Using sops as a library in a python script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -1,5 +1,5 @@
setuptools>=18.8.1
cryptography==1.2.1
cryptography==1.4
boto3>=1.1.3
ruamel.yaml==0.11.7
ordereddict>=1.1

View File

@@ -9,7 +9,7 @@ with codecs.open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
setup(
name="sops",
py_modules=['sops'],
version="1.13",
version="1.14",
author="Julien Vehent",
author_email="jvehent@mozilla.com",
description="Secrets OPerationS (sops) is an editor of encrypted files",
@@ -22,7 +22,7 @@ setup(
install_requires=[
'ruamel.yaml==0.11.7',
'boto3>=1.1.3',
'cryptography==1.2.1',
'cryptography==1.4',
'setuptools>=18.8.1',
'ordereddict>=1.1',
'simplejson>=3.8'],

View File

@@ -39,7 +39,7 @@ else:
if sys.version_info[0] == 3:
raw_input = input
VERSION = '1.13'
VERSION = '1.14'
DESC = """
`sops` supports AWS KMS and PGP encryption:
@@ -145,6 +145,12 @@ def main():
help="extract a specific key or branch from the "
"input JSON or YAML document. (decrypt mode "
"only). ex: --extract '[\"somekey\"][0]'")
argparser.add_argument('--set', dest='set', nargs=2,
help="set a specific key or branch in the "
"input JSON or YAML document. value must be "
"a json encoded string. (edit mode only)."
"ex: --set '[\"somekey\"][0]' "
"'{\"somevalue\":true}'")
argparser.add_argument('--input-type', dest='input_type',
help="input type (yaml, json, ...), "
"if undef, use file extension")
@@ -301,62 +307,77 @@ def main():
if not args.show_master_keys:
tree.pop('sops', None)
# the decrypted tree is written to a tempfile and an editor
# is opened on the file
tmppath = write_file(tree, filetype=otype)
tmphash = get_file_hash(tmppath)
print("INFO: temp file created at %s" % tmppath, file=sys.stderr)
if args.set:
# extract args for --set
set_path, json_value = args.set
value = json.loads(json_value)
# find set location
parent = truncate_tree(tree, set_path.rsplit('[', 1)[0])
# find set key
set_key = tree_path_comp(set_path.rsplit('[', 1)[1], set_path)
# set value
parent[set_key] = value
# restore sops from stash
tree['sops'] = stash['sops']
else:
# the decrypted tree is written to a tempfile and an editor
# is opened on the file
tmppath = write_file(tree, filetype=otype)
tmphash = get_file_hash(tmppath)
print("INFO: temp file created at %s" % tmppath, file=sys.stderr)
# open an editor on the file and, if the file is yaml or json,
# verify that it doesn't contain errors before continuing
valid_syntax = False
has_master_keys = False
while not valid_syntax or not has_master_keys:
run_editor(tmppath)
try:
valid_syntax = validate_syntax(tmppath, otype)
except Exception as e:
# open an editor on the file and, if the file is yaml or json,
# verify that it doesn't contain errors before continuing
valid_syntax = False
has_master_keys = False
while not valid_syntax or not has_master_keys:
run_editor(tmppath)
try:
print("ERROR: invalid syntax: %s\nPress a key to return into "
"the editor, or ctrl+c to exit without saving." % e,
file=sys.stderr)
raw_input()
except KeyboardInterrupt:
os.remove(tmppath)
panic("ctrl+c captured, exiting without saving", 85)
continue
valid_syntax = validate_syntax(tmppath, otype)
except Exception as e:
try:
print("ERROR: invalid syntax: %s\nPress a key to return "
"into the editor, or ctrl+c to exit without "
"saving." % e,
file=sys.stderr)
raw_input()
except KeyboardInterrupt:
os.remove(tmppath)
panic("ctrl+c captured, exiting without saving", 85)
continue
if args.show_master_keys:
# use the sops data from the file
tree = load_file_into_tree(tmppath, otype)
else:
# sops branch was removed for editing, restoring it
tree = load_file_into_tree(tmppath, otype,
restore_sops=stash['sops'])
if check_master_keys(tree):
has_master_keys = True
else:
try:
print("ERROR: could not find a valid master key to encrypt the"
" data key with.\nAdd at least one KMS or PGP "
"master key to the `sops` branch,\nor ctrl+c to "
"exit without saving.")
raw_input()
except KeyboardInterrupt:
os.remove(tmppath)
panic("ctrl+c captured, exiting without saving", 85)
if args.show_master_keys:
# use the sops data from the file
tree = load_file_into_tree(tmppath, otype)
else:
# sops branch was removed for editing, restoring it
tree = load_file_into_tree(tmppath, otype,
restore_sops=stash['sops'])
if check_master_keys(tree):
has_master_keys = True
else:
try:
print("ERROR: could not find a valid master key to encrypt"
" the data key with.\nAdd at least one KMS or PGP "
"master key to the `sops` branch,\nor ctrl+c to "
"exit without saving.")
raw_input()
except KeyboardInterrupt:
os.remove(tmppath)
panic("ctrl+c captured, exiting without saving", 85)
# verify if file has been modified, and if not, just exit
if tmphash == get_file_hash(tmppath):
os.remove(tmppath)
panic("%s has not been modified, exit without writing" % args.file,
error_code=200)
# verify if file has been modified, and if not, just exit
if tmphash == get_file_hash(tmppath):
os.remove(tmppath)
panic("%s has not been modified, exit without writing" % args.file,
error_code=200)
tree = walk_and_encrypt(tree, key, stash=stash)
tree = add_new_master_keys(tree, args.add_kms, args.add_pgp)
tree = remove_master_keys(tree, args.rm_kms, args.rm_pgp)
tree = update_master_keys(tree, key)
os.remove(tmppath)
if not args.set:
os.remove(tmppath)
# always store encrypted binary files in a json enveloppe
if otype == "bytes":
@@ -1345,18 +1366,23 @@ def truncate_tree(tree, path):
for comp in comps:
if comp == "":
continue
if comp[len(comp)-1] != "]":
panic("invalid tree path format: tree"+path, 91)
comp = comp[0:len(comp)-1]
comp = comp.replace('"', '', 2)
comp = comp.replace("'", "", 2)
if re.search(b'^\d+$', comp.encode('utf-8')):
tree = tree[int(comp)]
else:
tree = tree[comp]
comp = tree_path_comp(comp, path)
tree = tree[comp]
return tree
def tree_path_comp(comp, path):
if comp[len(comp)-1] != "]":
panic("invalid tree path format: tree"+path, 91)
comp = comp[0:len(comp)-1]
comp = comp.replace('"', '', 2)
comp = comp.replace("'", "", 2)
if re.search(b'^\d+$', comp.encode('utf-8')):
return int(comp)
else:
return comp
def to_bytes(value):
if not isinstance(value, bytes):
# if not bytes, convert to bytes