========= abraxas ========= ------------------------------ collaborative password utility ------------------------------ :Author: Kale and Ken Kundert :Date: 2016-08-14 :Version: 1.8 :Manual section: 3 .. :Copyright: Kale and Ken Kundert .. :Manual group: Utilities 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)