diff --git a/example.vaultpass.xml b/example.vaultpass.xml index cd7c524..bf302fc 100644 --- a/example.vaultpass.xml +++ b/example.vaultpass.xml @@ -3,7 +3,7 @@ xmlns="https://git.square-r00t.net/VaultPass/" xsi:schemaLocation="https://git.square-r00t.net/VaultPass/ http://schema.xml.r00t2.io/projects/vaultpass.xsd" autoUnseal="true" - unsealShard="YOUR_UNSEAL_HERE"> + unsealShard="WU9VUiBVTlNFQUwgU0hBUkQgSEVSRQo="> https://localhost:8000/ diff --git a/vaultpass/__init__.py b/vaultpass/__init__.py index e0ab1a9..6ecba3b 100644 --- a/vaultpass/__init__.py +++ b/vaultpass/__init__.py @@ -1,11 +1,50 @@ -import warnings -## -import hvac +import logging ## +from . import auth from . import clipboard from . import config +from . import logger + + +_logger = logging.getLogger('VaultPass') class PassMan(object): - def __init__(self): + client = None + auth = None + uri = None + + def __init__(self, cfg = '~/.config/vaultpass.xml'): + self.cfg = config.getConfig(cfg) + self.cfg.main() + self._getURI() + self.getClient() + + def _getURI(self): + uri = self.cfg.xml.find('uri') + if uri is None: + uri = 'http://localhost:8000/' pass + + def getClient(self): + # This may need to be re-tooled in the future. + auth_xml = self.cfg.xml.find('auth') + authmethod_xml = auth_xml.getchildren()[0]\ + for a in dir(auth): + if a.startswith('_'): + continue + c = getattr(auth, a) + if not c: + continue + confname = getattr(c, 'config_name') + if not confname or confname != authmethod_xml.tag: + continue + self.auth = c(self.uri, + authmethod_xml) + break + if not self.auth: + _logger.error('Invalid auth configuration') + _logger.debug('Auth specified ({0}) was not found or is not supported'.format(authmethod_xml.tag)) + raise RuntimeError('Invalid auth configuration') + self.client = self.auth.client + return(None) diff --git a/vaultpass/auth.py b/vaultpass/auth.py new file mode 100644 index 0000000..d522985 --- /dev/null +++ b/vaultpass/auth.py @@ -0,0 +1,130 @@ +import logging +import os +## +import hvac + + +_logger = logging.getLogger() + + +class _AuthBase(object): + name = '_AuthBase' + client = None + + def __init__(self, uri, auth_xml, *args, **kwargs): + self.uri = uri + self.xml = auth_xml + _logger.debug('Intialized instance of {0}'.format(self.name)) + + def authCheck(self): + if not self.client.is_authenticated(): + _logger.debug('Could not authenticate to {0} using {1}.'.format(self.uri, self.name)) + _logger.error('Could not authenticate') + raise RuntimeError('Could not authenticate') + + def getClient(self): + pass # Dummy/placeholder func. + return(None) + + +class AppRole(_AuthBase): + name = 'AppRole' + config_name = 'appRole' + role = None + secret = None + + def __init__(self, uri, auth_xml, *args, **kwargs): + super().__init__(uri, auth_xml, *args, **kwargs) + self.getClient() + + def getClient(self): + self.role = self.xml.find('role').text + self.secret = self.xml.find('secret').text + self.client = hvac.Client(url = self.uri) + self.client.auth_approle(self.role, secret_id = self.secret) + self.authCheck() + return(None) + + +class LDAP(_AuthBase): + name = 'LDAP' + config_name = 'ldap' + username = None + password = None + mount = None + + def __init__(self, uri, auth_xml, *args, **kwargs): + super().__init__(uri, auth_xml, *args, **kwargs) + self.getClient() + + def getClient(self): + self.username = self.xml.find('username').text + self.password = self.xml.find('password').text + _mntpt = self.xml.find('mountPoint') + if _mntpt is not None: + self.mount = _mntpt.text + else: + self.mount = 'ldap' + self.client = hvac.Client(url = self.uri) + self.client.auth.ldap.login(username = self.username, + password = self.password, + mount_point = self.mount) + self.authCheck() + return(None) + + +class Token(_AuthBase): + name = 'Token' + config_name = 'token' + token = None + + def __init__(self, uri, auth_xml, *args, **kwargs): + super().__init__(uri, auth_xml, *args, **kwargs) + self.getClient() + + def _getEnv(self, env_var): + var = os.environ.get(env_var) + if not var: + _logger.debug(('Environment variable {0} was specified as containing the token ' + 'but it is empty').format(env_var)) + _logger.error('Env var not populated') + raise RuntimeError('Env var not populated') + return(var) + + def _getFile(self, fpath): + fpath = os.path.abspath(os.path.expanduser(fpath)) + with open(fpath, 'r') as fh: + contents = fh.read().strip() + return(contents) + + def getClient(self): + _token = self.xml.text + if _token is not None: + self.token = _token + else: + # First we check the attrib. + a = self.xml.attrib.get('source') + if not a: + _exhausted = False + # try, in order, env var and then ~/.vault-token + while not _exhausted: + try: + self._getEnv('VAULT_TOKEN') + break + except Exception as e: + pass + try: + self._getFile('~/.vault-token') + except Exception as e: + _exhausted = True + if not self.token: + _logger.debug(('Unable to automatically determine token from ' + 'environment variable or filesystem defaults')) + _logger.error('Cannot determine token') + raise RuntimeError('Cannot determine token') + else: + if a.startswith('env:'): + e = a.split(':', 1) + self.token = self._getEnv(e) + else: + self.token = self._getFile(a) diff --git a/vaultpass/config.py b/vaultpass/config.py index 80c7d7d..7b0efa1 100644 --- a/vaultpass/config.py +++ b/vaultpass/config.py @@ -8,7 +8,7 @@ from lxml import etree # TODO: change filehandler of logger? https://stackoverflow.com/a/47447444 -_logger = logging.getLogger('config:{0}'.format(__name__)) +_logger = logging.getLogger() class Config(object): diff --git a/vaultpass/logger.py b/vaultpass/logger.py index 580864b..1ee6523 100644 --- a/vaultpass/logger.py +++ b/vaultpass/logger.py @@ -17,6 +17,10 @@ def prepLogfile(path = logfile): path = os.path.abspath(os.path.expanduser(path)) # Set up the permissions beforehand. os.makedirs(os.path.dirname(logfile), exist_ok = True, mode = 0o0700) + if not os.path.isfile(path): + # "Touch" it so the next command doesn't fail. + with open(path, 'w') as fh: + fh.write('') os.chmod(logfile, 0o0600) return(path) @@ -49,6 +53,6 @@ h.setFormatter(logging.Formatter(style = '{', _cfg_args['handlers'].append(h) logging.basicConfig(**_cfg_args) -logger = logging.getLogger() +logger = logging.getLogger('VaultPass') logger.info('Logging initialized.')