abraxas
collaborative password utility
- Author:
Kale and Ken Kundert <abraxas@nurdletech.com>
- Date:
2016-08-14
- Version:
1.8
- Manual section:
3
DESCRIPTION
The API to Abraxas will be simply demonstrated by example.
archive
This program is used to generate an encrypted file that includes the account numbers and login information for essential accounts. The resulting file could be sent to your Executor or it could be printed and saved in a safe place such as a safe deposit box. The idea is that this information would help whoever needed to access your accounts in case something happened to you.
Here is the archive script:
#!/bin/env python3
from __future__ import print_function, division
from abraxas import PasswordGenerator, PasswordError, Logging
from textwrap import indent
import gnupg
import sys
filename = 'kids.gpg'
recipients = [
'me@myfamily.name',
'son@myfamily.name',
'daughter@myfamily.name']
accounts = [
('login', 'Login'),
('disk', 'Disk encryption'),
('gpg', 'GPG'),
('boa', 'Bank of America'),
('tdwaterhouse', 'TD Waterhouse')]
try:
logger = Logging(exception=PasswordError)
pw = PasswordGenerator(logger=logger)
pw.read_accounts()
lines = []
for name, description in accounts:
lines += ["%s:" % (description if description else name)]
acct = pw.get_account(name)
# Remarks
remarks = acct.get_field('remarks')
if remarks:
if '\n' in remarks:
lines += [" remarks:"]
lines += [indent(remarks.strip(), ' ')]
else:
lines += [" remarks: " + remarks.strip()]
# Account number
account = acct.get_field('account')
if account:
if type(account) == list:
lines += [" account numbers:"]
lines += [" %s" % ',\n '.join(account)]
else:
lines += [" account number:", account]
# Username
username = acct.get_field('username')
if username:
lines += [" username:", username]
# Password
password = pw.generate_password()
if password:
lines += [" password:", password]
# Security questions
number = 0
security_questions = []
while True:
try:
question, answer = pw.generate_answer(number)
security_questions += [" %s ==> %s" % (question, answer)]
number += 1
except PasswordError:
break
if security_questions:
lines += [' security questions:']
lines += security_questions
lines += []
gpg = gnupg.GPG()
encrypted = gpg.encrypt('\n'.join(lines), recipients)
if not encrypted.ok:
sys.exit("%s: unable to encrypt.\n%s" % (filename, encrypted.stderr))
try:
with open(filename, 'w') as file:
file.write(str(encrypted))
print("%s: created." % filename)
except IOError as err:
sys.exit('%s: %s.' % (err.filename, err.strerror))
except KeyboardInterrupt:
sys.exit('Killed by user')
except PasswordError as err:
sys.exit(str(err))
The program starts by creating a logger. Normally this is not necessary. When you run PasswordGenerator() without passing in a logger the default logger is created for you. However, the default logger does not throw exceptions. Instead, when a problem occurs an error message is printed to standard error and the program exits. However, this utility needs exceptions to be caught and handled, and so in this case a logger is explicitly created and PasswordError is passed in. In this way, Abraxas does not exit on an error, instead it throws a PasswordError.
mountall
Here is a program that mounts a series of directories. It differs from the above script in that is uses autotype, which it accesses through AutotypeWriter. Specifically, the program never requests a password directly from Abraxas. Instead, the PasswordGenerator object is passed in when creating a AutotypeWriter object. It then queries the generator directly for the password and then gets it directly to the user.
Mountall uses sudo, which requires a password the first time it is run, and it runs mount for each directory, which requires a password each time it is run.
Here is the mountall script:
#!/bin/env python
from __future__ import print_function, division
from fileutils import expandPath, makePath, ShellExecute as Execute, ExecuteError
from sys import exit
from os import fork
from time import sleep
from abraxas import PasswordGenerator, AutotypeWriter, PasswordError
shares = {
'music': 'audio',
'lib/passwords': True,
'business': True,
'consulting': True,
'home': True,
'personal': True,
'photos': True,
'profession': True,
'reference': True}
def run_cmd_with_password(cmd, pw_writer):
try:
if (fork()):
Execute(cmd)
else:
sleep(1)
pw_writer.write_autotype()
pw_writer.process_output()
exit()
except PasswordError as err:
exit(err.message)
try:
# Open the password generator
pw = PasswordGenerator()
pw.read_accounts()
writer = AutotypeWriter(pw)
# Clear out any saved sudo credentials. This is needed so that
# we can be sure the next run of sudo requests a password.
# Without this, the password that is autotyped may be exposed.
Execute('sudo -K')
# Get the login password
pw.get_account('login')
# Run sudo so that it requests the password and sets the
# credentials. In this way the subsequent calls to sudo will not
# request a password.
run_cmd_with_password('sudo true', writer)
# Get the Samba password
pw.get_account('dgc21')
for src, dest in shares.items():
if dest == True:
dest = src
absdest = expandPath(makePath('~', dest))
mountpoint = pipe('mountpoint -q %s' % absdest, accept=(0,1))
if mountpoint.status:
print("Mounting %s to %s" % (src, absdest))
run_cmd_with_password('sudo mount %s' % (absdest), writer)
else:
print("Skipping %s (already mounted)" % (dest))
except KeyboardInterrupt:
exit('Killed by user')
except ExecuteError as err:
exit(str(err))
except PasswordError, err:
sys.exit(str(err))
The program starts by instantiating both the PasswordGenerator and the AutotypeWriter class. The PasswordGenerator class is responsible for generating the password and AutotypeWriter gets it to the user. In this case the autotype facility is used to mimic the keyboard. There are other writers available for writing to a TTY, to stdout, and to the system clipboard.
addkeys
This script is used to pre-load a series of SSH keys into the SSH agent. It is stimilar to the above script, except it uses pexpect rather than autotype. This makes it a bit safer because pexpect waits for the expected prompt from ssh-add, and so will not blindly spew out the password if things go wrong:
#!/usr/bin/python3
import pexpect
from abraxas import PasswordGenerator, PasswordError
import sys
keys = [
# description keyfile abraxas account name
('primary rsa', 'id-rsa', 'ssh' ),
('primary ed25519', 'id-ed25519', 'ssh' ),
('digitalocean', 'digitalocean', 'do-ssh' ),
('tunnelr', 'tunnelr', 'tunnelr-ssh' ),
('dumper', 'dumper', 'dumper' ),
('github', 'github', 'github-ssh' ),
]
ssh_dir = '/home/toby/.ssh'
try:
pw = PasswordGenerator()
pw.read_accounts()
except PasswordError as error:
sys.exit(str(error))
for desc, name, acct in keys:
print('Adding %s ssh key' % desc)
try:
acct = pw.get_account(acct)
password = pw.generate_password()
sshadd = pexpect.spawn('ssh-add %s/%s' % (ssh_dir, name))
sshadd.expect(
'Enter passphrase for %s/%s: ' % (ssh_dir, name),
timeout=4
)
sshadd.sendline(password)
sshadd.expect(pexpect.EOF)
sshadd.close()
if sshadd.exitstatus:
print('addkeys: ssh-add: unexpected exit status:', sshadd.exitstatus)
except PasswordError as error:
sys.exit(str(error))
except (pexpect.EOF, pexpect.TIMEOUT):
sys.exit('addkeys: unexpected prompt from ssh-add: %s' % (
sshadd.before.decode('utf8')
))
except KeyboardInterrupt:
exit('Killed by user')
SEE ALSO
abraxas(1), abraxas(5)