Skip to content
Snippets Groups Projects
__init__.py 5.56 KiB
Newer Older
This module persistently stores informion on tunnels in an in memory structure.
"""
import datetime
import yaml

class SSHSession:
    """Interfaces for working with processes forked from flask
    in particular, we fork processes for ssh-agent and ssh tunnels and execution
    def __init__(self,**kwargs):
        self.last = datetime.datetime.now()
        self.socket = None
        self.token = None
        self.key = ''
        self.cert = ''
        self.pids = []
        self.authtok = None
        self.__dict__.update(kwargs)
        self.sshadd = '/usr/bin/ssh-add'
        self.ctrl_processes = {}
    def start_agent(self):
        import subprocess
        p = subprocess.Popen([self.sshagent],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        (stdout,stderr) = p.communicate()
        for l in stdout.decode().split(';'):
            if 'SSH_AUTH_SOCK=' in l:
                socket = l.split('=')[1]
                self.socket = socket
            if 'SSH_AGENT_PID=' in l:
                pid = l.split('=')[1]
                self.pids.append(pid)
    def add_keycert(self,key,cert):
        import tempfile
        import os
        import subprocess
        import logging
        logger = logging.getLogger()
        if self.socket is None:
            logger.debug('adding a cert, start the agent first')
            self.start_agent()
            logger.debug('agent socket is now {}'.format(self.socket))
        keyf = tempfile.NamedTemporaryFile(mode='w',delete=False)
        keyname = keyf.name
        keyf.write(key)
        keyf.close()
        certf = open(keyname+'-cert.pub',mode='w')
        certf.write(cert)
        certf.close()
        env = os.environ.copy()
        env['SSH_AUTH_SOCK'] = self.socket
        cmd.append(keyname)
        p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env)
        (stdout,stderr) = p.communicate()
        if p.returncode != 0:
            logger.error("Couldn't add key and cert")
            logger.debug(stdout)
            logger.debug(stderr)
        os.unlink(keyname+'-cert.pub')
        os.unlink(keyname)
        import os
        import subprocess
        import logging
        logger=logging.getLogger()
        if self.socket is None:
            return res
        env = os.environ.copy()
        env['SSH_AUTH_SOCK'] = self.socket
        cmd = [self.sshadd,'-L']
        p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env)
        (stdout,stderr) = p.communicate()
        if stderr is not None:
            logger.debug('called sshadd, got stderr {}'.format(stderr))
        if stdout is not None:
            logger.debug('called sshadd, got stdout {}'.format(stdout))
        for l in stdout.splitlines():
            logger.debug('is {} a cert?'.format(l))
            if b'cert' in l:
                logger.debug('decoding {}'.format(l))
                p = subprocess.Popen([self.sshkeygen,'-L','-f','-'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                keygenout,keygenerr = p.communicate(l)
                certcontents = SSHSession.parse_cert_contents(keygenout.decode().splitlines())
                res.append(certcontents)
            else: 
                res.append({'pubkey':l.decode()})
        return res

    @staticmethod
    def parse_cert_contents(lines):
        key = None
        values = []
        res = {}
        for l in lines:
            if ':' in l:
                if key is not None:
                    res[key] = values
                values = []
                (key,v) = l.split(':',1)
                if v is not '':
                if l is not '':
                    values.append(l)
    def refresh(self):
        import datetime
        self.last = datetime.datetime.now()
    # def set_authtok(self,authtok):
    #     self.authtok = authtok
    def addkey(self,key,cert):
        pass

    def kill(self):
        import os
        import signal
        import logging
        logger=logging.getLogger()
        logger.debug("shuting down ssh session for {} last seen at {}".format(self.authtok,self.last))
        for pid in self.pids:
            try:
                os.kill(int(pid), signal.SIGTERM)
            except ProcessLookupError as e:
Ubuntu's avatar
Ubuntu committed
                logger.debug("process {} not found".format(pid))
        for ctrl in self.ctrl_processes.items():
            ctrl[1].kill()
            ctrl[1].wait(5)
            os.unlink(ctrl[0])
    def get_sshsession():
        import random
        import string
        from .. import sshsessions
        from flask import session
        sshsessid = session.get('sshsessid', None)
        N = 8
        while sshsessid is None:
            key = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
            if key not in session:
                sshsessid = key
                session['sshsessid'] = sshsessid
        if sshsessid not in sshsessions:
            sshsessions[sshsessid] = SSHSession()
        return sshsessions[sshsessid]

    @staticmethod
    def remove_sshsession():
        import random
        import string
        from .. import sshsessions
        from flask import session
        sshsessid = session.get('sshsessid', None)
        del sshsessions[sshsessid]