Merge pull request #22 from p-hash/master

Add support for automatic key decryption
This commit is contained in:
Joao Jacome 2022-04-29 14:39:33 +01:00 committed by GitHub
commit e348bcc050
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 11 deletions

View file

@ -13,16 +13,18 @@ Fetches SSH keys stored in Bitwarden vault and adds them to `ssh-agent`.
./bw_add_sshkeys.py ./bw_add_sshkeys.py
``` ```
2. Enter your Bitwarden credentials, if a Bitwarden vault session is not already set. 2. Enter your Bitwarden credentials, if a Bitwarden vault session is not already set.
3. (optional) Enter your SSH keys' passphrases. 3. (optional) Enter your SSH keys' passphrases if they're not stored in your Bitwarden.
## Storing the keys in BitWarden ## Storing the keys in BitWarden
1. Create a folder called `ssh-agent` (can be overridden on the command line). 1. Create a folder called `ssh-agent` (can be overridden on the command line).
2. Add an new secure note to that folder. 2. Add an new secure note to that folder.
3. Upload the private key as an attachment. 3. Upload the private key as an attachment.
4. Add the custom field `private` (can be overridden on the command line), containing the file name of the private key attachment. 4. Add the custom field `private` (can be overridden on the command line), containing the file name of the private key attachment.
5. Repeat steps 2-4 for each subsequent key 5. (optional) If your key is encrypted with passphrase and you want it to decrypt automatically, save passphrase into custom field `passphrase` (field name can be overriden on the command line)
6. Repeat steps 2-5 for each subsequent key
## Command line overrides ## Command line overrides
* `--debug`/`-d` - Show debug output * `--debug`/`-d` - Show debug output
* `--foldername`/`-f` - Folder name to use to search for SSH keys _(default: ssh-agent)_ * `--foldername`/`-f` - Folder name to use to search for SSH keys _(default: ssh-agent)_
* `--customfield`/`-c` - Custom field name where private key filename is stored _(default: private)_ * `--customfield`/`-c` - Custom field name where private key filename is stored _(default: private)_
* `--passphrasefield`/`-p` - Custom field name where passphrase for the key is stored _(default: passphrase)_

View file

@ -135,7 +135,7 @@ def folder_items(session: str, folder_id: str) -> List[Dict[str, Any]]:
return data return data
def add_ssh_keys(session: str, items: List[Dict[str, Any]], keyname: str) -> None: def add_ssh_keys(session: str, items: List[Dict[str, Any]], keyname: str, pwkeyname: str) -> None:
""" """
Function to attempt to get keys from a vault item Function to attempt to get keys from a vault item
""" """
@ -144,7 +144,7 @@ def add_ssh_keys(session: str, items: List[Dict[str, Any]], keyname: str) -> Non
private_key_file = [ private_key_file = [
k['value'] k['value']
for k in item['fields'] for k in item['fields']
if k['name'] == keyname and k['type'] == 0 if k['name'] == keyname
][0] ][0]
except IndexError: except IndexError:
logging.warning('No "%s" field found for item %s', keyname, item['name']) logging.warning('No "%s" field found for item %s', keyname, item['name'])
@ -156,6 +156,21 @@ def add_ssh_keys(session: str, items: List[Dict[str, Any]], keyname: str) -> Non
continue continue
logging.debug('Private key file declared') logging.debug('Private key file declared')
private_key_pw = None
try:
private_key_pw = [
k['value']
for k in item['fields']
if k['name'] == pwkeyname
][0]
logging.debug('Passphrase declared')
except IndexError:
logging.warning('No "%s" field found for item %s', pwkeyname, item['name'])
except KeyError as error:
logging.debug(
'No key "%s" found in item %s - skipping', error.args[0], item['name']
)
try: try:
private_key_id = [ private_key_id = [
k['id'] k['id']
@ -172,12 +187,12 @@ def add_ssh_keys(session: str, items: List[Dict[str, Any]], keyname: str) -> Non
logging.debug('Private key ID found') logging.debug('Private key ID found')
try: try:
ssh_add(session, item['id'], private_key_id) ssh_add(session, item['id'], private_key_id, private_key_pw)
except subprocess.SubprocessError: except subprocess.SubprocessError:
logging.warning('Could not add key to the SSH agent') logging.warning('Could not add key to the SSH agent')
def ssh_add(session: str, item_id: str, key_id: str) -> None: def ssh_add(session: str, item_id: str, key_id: str, key_pw: str) -> None:
""" """
Function to get the key contents from the Bitwarden vault Function to get the key contents from the Bitwarden vault
""" """
@ -202,14 +217,18 @@ def ssh_add(session: str, item_id: str, key_id: str) -> None:
) )
ssh_key = proc_attachment.stdout ssh_key = proc_attachment.stdout
logging.debug("Running ssh-add") if key_pw:
envdict = dict(os.environ, SSH_ASKPASS=os.path.realpath(__file__), SSH_KEY_PASSPHRASE=key_pw)
else:
envdict = dict(os.environ, SSH_ASKPASS_REQUIRE="never")
logging.debug("Running ssh-add")
# CAVEAT: `ssh-add` provides no useful output, even with maximum verbosity # CAVEAT: `ssh-add` provides no useful output, even with maximum verbosity
subprocess.run( subprocess.run(
['ssh-add', '-'], ['ssh-add', '-'],
input=ssh_key, input=ssh_key,
# Works even if ssh-askpass is not installed # Works even if ssh-askpass is not installed
env=dict(os.environ, SSH_ASKPASS_REQUIRE="never"), env=envdict,
universal_newlines=True, universal_newlines=True,
check=True, check=True,
) )
@ -240,6 +259,12 @@ if __name__ == '__main__':
default='private', default='private',
help='custom field name where private key filename is stored', help='custom field name where private key filename is stored',
) )
parser.add_argument(
'-p',
'--passphrasefield',
default='passphrase',
help='custom field name where key passphrase is stored'
)
return parser.parse_args() return parser.parse_args()
@ -269,10 +294,13 @@ if __name__ == '__main__':
items = folder_items(session, folder_id) items = folder_items(session, folder_id)
logging.info('Attempting to add keys to ssh-agent') logging.info('Attempting to add keys to ssh-agent')
add_ssh_keys(session, items, args.customfield) add_ssh_keys(session, items, args.customfield, args.passphrasefield)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
if error.stderr: if error.stderr:
logging.error('`%s` error: %s', error.cmd[0], error.stderr) logging.error('`%s` error: %s', error.cmd[0], error.stderr)
logging.debug('Error running %s', error.cmd) logging.debug('Error running %s', error.cmd)
if os.environ.get('SSH_ASKPASS'):
print(os.environ.get('SSH_KEY_PASSPHRASE'))
else:
main() main()