Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support AWS KMS Encryption Context #76

Merged
merged 6 commits into from
Aug 22, 2016
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions sops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ def main():
help="path to config file, disable recursive search"
" (default: {default})"
.format(default=DEFAULT_CONFIG_FILE))
argparser.add_argument('--encryption-context', dest='context',
help="KMS encryption context: "
"key-value pair dict encoded in JSON")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't used KMS encryption contexts, but would there be a way to expand this to not require JSON on the command line but take individual parameters instead? We already have a custom csv format for specifying multiple kms and pgp on the command line, and I'd prefer to avoid introducing another format.

argparser.add_argument('-V', '-v', '--version', action=ShowVersion,
version='%(prog)s ' + str(VERSION))
args = argparser.parse_args()
Expand Down Expand Up @@ -206,10 +209,17 @@ def main():
else:
otype = itype

# encryption context if any
if args.context:
context = json.loads(args.context)
else:
context = None

tree, need_key, existing_file = initialize_tree(args.file, itype,
kms_arns=kms_arns,
pgp_fps=pgp_fps,
configloc=args.config_loc)
configloc=args.config_loc,
context=context)
if not existing_file:
# can't use add/rm keys on new files, they don't yet have keys
if args.add_kms or args.add_pgp or args.rm_kms or args.rm_pgp:
Expand Down Expand Up @@ -361,7 +371,8 @@ def detect_filetype(filename):
return 'bytes'


def initialize_tree(path, itype, kms_arns=None, pgp_fps=None, configloc=None):
def initialize_tree(path, itype, kms_arns=None, pgp_fps=None, configloc=None,
context=None):
""" Try to load the file from path in a tree, and failing that,
initialize a new tree using default data
"""
Expand All @@ -378,7 +389,8 @@ def initialize_tree(path, itype, kms_arns=None, pgp_fps=None, configloc=None):
kms_arns=kms_arns,
pgp_fps=pgp_fps,
path=path,
configloc=configloc)
configloc=configloc,
context=context)
# try to set the input version to the one set in the file
try:
global INPUT_VERSION
Expand Down Expand Up @@ -406,7 +418,8 @@ def initialize_tree(path, itype, kms_arns=None, pgp_fps=None, configloc=None):
if config:
kms_arns = config.get("kms", None)
pgp_fps = config.get("pgp", None)
tree, need_key = verify_or_create_sops_branch(tree, kms_arns, pgp_fps)
tree, need_key = verify_or_create_sops_branch(tree, kms_arns, pgp_fps,
context=context)
return tree, need_key, existing_file


Expand Down Expand Up @@ -499,7 +512,7 @@ def find_config_for_file(filename, configloc):


def verify_or_create_sops_branch(tree, kms_arns=None, pgp_fps=None,
path=None, configloc=None):
path=None, configloc=None, context=None):
"""Verify or create the sops branch in the tree.

If the current tree doesn't have a sops branch with either kms or pgp
Expand Down Expand Up @@ -545,6 +558,9 @@ def verify_or_create_sops_branch(tree, kms_arns=None, pgp_fps=None,
tree, has_at_least_one_method = parse_kms_arn(tree, kms_arns)
if pgp_fps:
tree, has_at_least_one_method = parse_pgp_fp(tree, pgp_fps)
if context:
for entry in tree['sops']['kms']:
entry['context'] = context
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is meant to be a dict. I'd suggest validating its format before storing it.

if not has_at_least_one_method:
panic("Error: No KMS ARN or PGP Fingerprint found to encrypt the data "
"key, read the help (-h) for more information.", 111)
Expand Down Expand Up @@ -1068,8 +1084,10 @@ def get_key_from_kms(tree):
errors.append("no kms client could be obtained for entry %s" %
entry['arn'])
continue
context = entry['context'] if 'context' in entry else {}
try:
kms_response = kms.decrypt(CiphertextBlob=b64decode(enc))
kms_response = kms.decrypt(CiphertextBlob=b64decode(enc),
EncryptionContext=context)
except Exception as e:
errors.append("kms %s failed with error: %s " % (entry['arn'], e))
continue
Expand All @@ -1090,8 +1108,10 @@ def encrypt_key_with_kms(key, entry):
print("ERROR: failed to initialize AWS KMS client for entry: %s" % err,
file=sys.stderr)
return None
context = entry['context'] if 'context' in entry else {}
try:
kms_response = kms.encrypt(KeyId=entry['arn'], Plaintext=key)
kms_response = kms.encrypt(KeyId=entry['arn'], Plaintext=key,
EncryptionContext=context)
except Exception as e:
print("ERROR: failed to encrypt key using kms arn %s: %s" %
(entry['arn'], e), file=sys.stderr)
Expand Down