mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
25
README.rst
25
README.rst
@@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
4
setup.py
4
setup.py
@@ -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'],
|
||||
|
||||
140
sops/__init__.py
140
sops/__init__.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user