CLI tool to unlock the XDG Secret collection used by 99designs/aws-vault
#!/usr/bin/env python3 | |
# | |
# Copyright © 2020 Pennock Tech, LLC | |
# SPDX-License-Identifier: MIT | |
""" | |
aws-vault-unlock: unlock (or lock) the awsvault libsecret collection | |
The XDG folks specify the Secret service available over D-Bus. | |
When everything works right, 99designs/aws-vault trying to access a locked | |
collection automatically prompts for it to be unlocked. | |
When things do not work right, it's time to manually unlock it. | |
After which, even after locking it, automatic unlocking should happen. | |
You could also install Seahorse and use a GUI, but if you have more than | |
"a very small number" of OpenPGP keys on your system, then you will likely | |
regret that approach. | |
""" | |
__author__ = 'phil@pennock-tech.com (Phil Pennock)' | |
import argparse | |
import contextlib | |
import sys | |
try: | |
import secretstorage # pip: SecretStorage | |
except ModuleNotFoundError as e: | |
print(f'{e}\n Suggestion: pip install SecretStorage\n') | |
sys.exit(1) | |
DEF_COLLECTION_NAME = 'awsvault' | |
COLLECTION_PATH_PREFIX = '/org/freedesktop/secrets/collection/' | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-l', '--lock', | |
action='store_true', default=False, | |
help='Lock secret collection/vault') | |
parser.add_argument('-u', '--unlock', | |
action='store_true', default=False, | |
help='Unlock secret collection/vault') | |
parser.add_argument('--list', | |
action='store_true', default=False, | |
help='List known collections') | |
parser.add_argument('--collection-name', | |
default=DEF_COLLECTION_NAME, | |
help='Collection alias to query [%(default)s]') | |
options = parser.parse_args() | |
if not (options.lock or options.unlock or options.list): | |
options.unlock = True | |
# Allow both unlock and lock: treat as desire to lock-then-unlock, bouncing | |
# the collection. Non-sensical perhaps, but do it. | |
with contextlib.closing(secretstorage.dbus_init()) as conn: | |
if options.list: | |
print('{:30} {:6} {}'.format('Collection', 'Locked', 'Path')) | |
LAYOUT='{!r:30} {:6} {!r}' | |
for collection in secretstorage.get_all_collections(conn): | |
print(LAYOUT.format(collection.get_label(), str(collection.is_locked()), collection.collection_path)) | |
sys.exit(0) | |
# collection = secretstorage.get_collection_by_alias(conn, options.collection_name) | |
# 'awsvault' is not an alias and I can find no way to look up a list of aliases, | |
# or any other introspection. Reading the 99designs/keyring Go code, they just iterate | |
# all the collections and look for a path match. | |
needle = COLLECTION_PATH_PREFIX + options.collection_name | |
candidates = [c for c in secretstorage.get_all_collections(conn) if c.collection_path == needle] | |
if len(candidates) == 0: | |
print(f'secret storage: collection {options.collection_name!r} NOT FOUND', file=sys.stderr) | |
sys.exit(1) | |
if len(candidates) > 1: | |
C = len(candidates) | |
print(f'secret storage: collection {options.collection_name!r} has {C} matches, assuming attack, aborting', file=sys.stderr) | |
sys.exit(1) | |
collection = candidates[0] | |
if options.lock: | |
if collection.is_locked(): | |
print(f'secret storage: collection {options.collection_name!r} was already locked', file=sys.stderr) | |
else: | |
collection.lock() | |
print(f'secret storage: collection {options.collection_name!r} now locked', file=sys.stderr) | |
if options.unlock: | |
if collection.is_locked(): | |
collection.unlock() | |
print(f'secret storage: collection {options.collection_name!r} now unlocked', file=sys.stderr) | |
else: | |
print(f'secret storage: collection {options.collection_name!r} was already unlocked', file=sys.stderr) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment