Skip to content

Instantly share code, notes, and snippets.

@philpennock
Created January 13, 2020 03:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philpennock/fc786f944d5bfa57991802c73e2efa1d to your computer and use it in GitHub Desktop.
Save philpennock/fc786f944d5bfa57991802c73e2efa1d to your computer and use it in GitHub Desktop.
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