Ejemplo n.º 1
0
    def display_search_result(self):
        """打印搜索的结果"""
        if self.assets is None:
            self.assets = self.get_my_assets()
        if not self.search_result:
            self.search_result = self.assets

        hostname_length = max_length(
            [asset.hostname for asset in self.search_result])
        system_user_length = system_user_max_length(self.assets)
        line = '[%-4s] %-16s %-5s %-' + str(hostname_length) + 's %-' + str(
            system_user_length + 2) + 's '
        comment_length = request.win_width - len(line % ((' ', ) * 5)) - 5
        line += ('%-' + str(comment_length) + 's')
        self.client_channel.send(
            wr(
                title(
                    line %
                    ('ID', 'IP', 'Port', 'Hostname', 'Username', 'Comment'))))

        for index, asset in enumerate(self.search_result):
            system_users = '[' + ', '.join(
                [system_user.username for system_user in asset.system_users]) \
                + ']'
            self.client_channel.send(
                wr(line % (index, asset.ip, asset.port, asset.hostname,
                           system_users, asset.comment)))
        self.client_channel.send(wr(''))
Ejemplo n.º 2
0
    def search_and_proxy(self, option, from_result=False):
        """搜索并登录资产"""
        self.search_assets(option=option, from_result=from_result)
        if len(self.search_result) == 1:
            request.asset = asset = self.search_result[0]
            if len(asset.system_users) == 1:
                system_user = asset.system_users[0]
            else:
                self.client_channel.send(
                    wr(primary('More than one system user granted, select one')))
                system_user = self.choose_system_user(asset.system_users)
                if system_user is None:
                    return self.dispatch()

            request.system_user = system_user
            self.return_to_proxy(asset, system_user)
        elif len(self.search_result) == 0:
            self.client_channel.send(
                wr(warning('No asset match, please input again')))
            return self.dispatch()
        else:
            self.client_channel.send(
                wr(primary('Search result is not unique, '
                           'select below or search again'), after=2))
            self.display_search_result()
            self.dispatch(twice=True)
Ejemplo n.º 3
0
    def search_and_proxy(self, option, from_result=False):
        """搜索并登录资产"""
        self.search_assets(option=option, from_result=from_result)
        if len(self.search_result) == 1:
            request.asset = asset = self.search_result[0]
            if len(asset.system_users) == 1:
                system_user = asset.system_users[0]
            else:
                self.client_channel.send(
                    wr(primary(
                        'More than one system user granted, select one')))
                system_user = self.choose_system_user(asset.system_users)
                if system_user is None:
                    return self.dispatch()

            request.system_user = system_user
            self.return_to_proxy(asset, system_user)
        elif len(self.search_result) == 0:
            self.client_channel.send(
                wr(warning('No asset match, please input again')))
            return self.dispatch()
        else:
            self.client_channel.send(
                wr(primary('Search result is not unique, '
                           'select below or search again'),
                   after=2))
            self.display_search_result()
            self.dispatch(twice=True)
Ejemplo n.º 4
0
    def choose_system_user(self, system_users):
        """当资产上有多个授权系统用户时, 让用户二次选择"""
        while True:
            for index, system_user in enumerate(system_users):
                self.client_channel.send(wr('[%s] %s' % (index, system_user.username)))

            option = self.get_input(prompt='System user> ')
            if option.isdigit() and int(option) < len(system_users):
                system_user = system_users[int(option)]
                return system_user
            elif option in ['q', 'Q']:
                return None
            else:
                self.client_channel.send(
                    wr(warning('No system user match, please input again')))
Ejemplo n.º 5
0
    def choose_system_user(self, system_users):
        """当资产上有多个授权系统用户时, 让用户二次选择"""
        while True:
            for index, system_user in enumerate(system_users):
                self.client_channel.send(wr('[%s] %s' % (index, system_user.username)))

            option = self.get_input(prompt='System user> ')
            if option.isdigit() and int(option) < len(system_users):
                system_user = system_users[int(option)]
                return system_user
            elif option in ['q', 'Q']:
                return None
            else:
                self.client_channel.send(
                    wr(warning('No system user match, please input again')))
Ejemplo n.º 6
0
 def display_asset_groups(self):
     """打印授权的资产组"""
     self.asset_groups = self.get_my_asset_groups()
     name_max_length = max_length([asset_group.name for asset_group
                                   in self.asset_groups], max_=20)
     comment_max_length = max_length([asset_group.comment for asset_group
                                      in self.asset_groups], max_=30)
     line = '[%-3s] %-' + str(name_max_length) + 's %-6s  %-' \
            + str(comment_max_length) + 's'
     self.client_channel.send(wr(title(line % ('ID', 'Name', 'Assets', 'Comment'))))
     for index, asset_group in enumerate(self.asset_groups):
         self.client_channel.send(wr(line % (
             index, asset_group.name,
             asset_group.assets_amount,
             asset_group.comment[:comment_max_length])))
     self.client_channel.send(wr(''))
Ejemplo n.º 7
0
 def display_asset_groups(self):
     """打印授权的资产组"""
     self.asset_groups = self.get_my_asset_groups()
     name_max_length = max_length([asset_group.name for asset_group
                                   in self.asset_groups], max_=20)
     comment_max_length = max_length([asset_group.comment for asset_group
                                      in self.asset_groups], max_=30)
     line = '[%-3s] %-' + str(name_max_length) + 's %-6s  %-' \
            + str(comment_max_length) + 's'
     self.client_channel.send(wr(title(line % ('ID', 'Name', 'Assets', 'Comment'))))
     for index, asset_group in enumerate(self.asset_groups):
         self.client_channel.send(wr(line % (
             index, asset_group.name,
             asset_group.assets_amount,
             asset_group.comment[:comment_max_length])))
     self.client_channel.send(wr(''))
Ejemplo n.º 8
0
    def process_request(self, client, addr):
        rc = self.request_context({'REMOTE_ADDR': addr[0]})
        rc.push()
        logger.info("Get ssh request from %s" % request.environ['REMOTE_ADDR'])
        transport = paramiko.Transport(client, gss_kex=False)
        try:
            transport.load_server_moduli()
        except:
            logger.warning('Failed to load moduli -- gex will be unsupported.')
            raise

        transport.add_server_key(SSHInterface.get_host_key())
        # 将app和请求上下文传递过去, ssh_interface 处理ssh认证和建立连接
        ssh_interface = SSHInterface(self, rc)

        try:
            transport.start_server(server=ssh_interface)
        except paramiko.SSHException:
            logger.warning('SSH negotiation failed.')
            sys.exit(1)

        _client_channel = transport.accept(20)
        g.client_channel = _client_channel
        if _client_channel is None:
            logger.warning('No ssh channel get.')
            sys.exit(1)

        if request.method == 'shell':
            logger.info('Client asked for a shell.')
            InteractiveServer(self).run()
        elif request.method == 'command':
            _client_channel.send(wr(warning('We are not support command now')))
            _client_channel.close()
            sys.exit(2)
        else:
            _client_channel.send(wr(warning('Not support the request method')))
            _client_channel.close()
            sys.exit(2)

        while True:
            if request.user is not None:
                break
            else:
                time.sleep(0.2)
Ejemplo n.º 9
0
 def display_asset_group_asset(self, option):
     """打印资产组下的资产"""
     match = re.match(r'g(\d+)', option)
     if match:
         index = match.groups()[0]
         if index.isdigit() and len(self.asset_groups) > int(index):
             asset_group = self.asset_groups[int(index)]
             self.search_result = self.user_service.\
                 get_assets_in_group(asset_group.id)
             self.display_search_result()
             self.dispatch(twice=True)
     self.client_channel.send(
         wr(warning('No asset group match, please input again')))
Ejemplo n.º 10
0
 def display_asset_group_asset(self, option):
     """打印资产组下的资产"""
     match = re.match(r'g(\d+)', option)
     if match:
         index = match.groups()[0]
         if index.isdigit() and len(self.asset_groups) > int(index):
             asset_group = self.asset_groups[int(index)]
             self.search_result = self.user_service.\
                 get_assets_in_group(asset_group.id)
             self.display_search_result()
             self.dispatch(twice=True)
     self.client_channel.send(wr(warning(
         'No asset group match, please input again')))
Ejemplo n.º 11
0
    def get_input(self, prompt='Opt> '):
        """实现了一个ssh input, 提示用户输入, 获取并返回"""
        input_data = []
        parser = TtyIOParser(request.win_width, request.win_height)
        self.client_channel.send(wr(prompt, before=1, after=0))
        while True:
            r, w, x = select.select([self.client_channel], [], [])
            if self.client_channel in r:
                data = self.client_channel.recv(1024)
                if data in self.BACKSPACE_CHAR:
                    # If input words less than 0, should send 'BELL'
                    if len(input_data) > 0:
                        data = self.BACKSPACE_CHAR[data]
                        input_data.pop()
                    else:
                        data = self.BELL_CHAR
                    self.client_channel.send(data)
                    continue

                if data.startswith(b'\x1b') or data in self.UNSUPPORTED_CHAR:
                    self.client_channel.send('')
                    continue

                # handle shell expect
                multi_char_with_enter = False
                if len(data) > 1 and data[-1] in self.ENTER_CHAR:
                    self.client_channel.send(data)
                    input_data.append(data[:-1])
                    multi_char_with_enter = True

                # If user type ENTER we should get user input
                if data in self.ENTER_CHAR or multi_char_with_enter:
                    self.client_channel.send(wr('', after=2))
                    option = parser.parse_input(b''.join(input_data))
                    return option.strip()
                else:
                    self.client_channel.send(data)
                    input_data.append(data)
Ejemplo n.º 12
0
    def display_search_result(self):
        """打印搜索的结果"""
        if self.assets is None:
            self.assets = self.get_my_assets()
        if not self.search_result:
            self.search_result = self.assets

        hostname_length = max_length([asset.hostname for asset in self.search_result])
        system_user_length = system_user_max_length(self.assets)
        line = '[%-4s] %-16s %-5s %-' + str(hostname_length) + 's %-' + str(system_user_length+2) + 's '
        comment_length = request.win_width-len(line % ((' ',) * 5)) - 5
        line += ('%-' + str(comment_length) + 's')
        self.client_channel.send(wr(title(line % (
            'ID', 'IP', 'Port', 'Hostname', 'Username', 'Comment'))))

        for index, asset in enumerate(self.search_result):
            system_users = '[' + ', '.join(
                [system_user.username for system_user in asset.system_users]) \
                + ']'
            self.client_channel.send(wr(
                line % (index, asset.ip, asset.port, asset.hostname,
                        system_users, asset.comment)))
        self.client_channel.send(wr(''))
Ejemplo n.º 13
0
    def get_input(self, prompt='Opt> '):
        """实现了一个ssh input, 提示用户输入, 获取并返回"""
        input_data = []
        parser = TtyIOParser(request.win_width, request.win_height)
        self.client_channel.send(wr(prompt, before=1, after=0))
        while True:
            data = self.client_channel.recv(1024)
            if data in self.BACKSPACE_CHAR:
                # If input words less than 0, should send 'BELL'
                if len(input_data) > 0:
                    data = self.BACKSPACE_CHAR[data]
                    input_data.pop()
                else:
                    data = self.BELL_CHAR
                self.client_channel.send(data)
                continue

            if data.startswith(b'\x1b') or data in self.UNSUPPORTED_CHAR:
                self.client_channel.send('')
                continue

            # handle shell expect
            multi_char_with_enter = False
            if len(data) > 1 and data[-1] in self.ENTER_CHAR:
                self.client_channel.send(data)
                input_data.append(data[:-1])
                multi_char_with_enter = True

            # If user type ENTER we should get user input
            if data in self.ENTER_CHAR or multi_char_with_enter:
                self.client_channel.send(wr('', after=2))
                option = parser.parse_input(b''.join(input_data))
                return option.strip()
            else:
                self.client_channel.send(data)
                input_data.append(data)
Ejemplo n.º 14
0
    def connect(self, term=b'xterm', width=80, height=24, timeout=10):
        user = self.user
        asset = self.asset
        system_user = self.system_user
        client_channel = self.client_channel
        try:
            # Todo: win_width in request or client_channel
            width = int(client_channel.win_width)
            height = int(client_channel.win_height)
        except TypeError:
            pass
        if not self.validate_user_asset_permission():
            logger.warning('User %s have no permission connect %s with %s' %
                           (user.username, asset.ip, system_user.username))
            return None
        self.ssh = ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        password, private_key = self.get_asset_auth(system_user)

        data = {
            "user": user.username,
            "asset": asset.ip,
            "system_user": system_user.username,
            "login_type": "ST",
            "date_start": time.time(),
            "is_failed": 0
        }
        self.proxy_log_id = proxy_log_id = self.service.send_proxy_log(data)
        self.app.proxy_list[
            proxy_log_id] = self.client_channel, self.backend_channel
        try:
            client_channel.send(
                wr('Connecting %s@%s:%s ... ' %
                   (system_user.username, asset.ip, asset.port)))
            ssh.connect(hostname=asset.ip,
                        port=asset.port,
                        username=system_user.username,
                        password=password,
                        pkey=private_key,
                        look_for_keys=False,
                        allow_agent=True,
                        compress=True,
                        timeout=timeout)

        except (paramiko.AuthenticationException,
                paramiko.ssh_exception.SSHException):
            msg = 'Connect backend server %s failed: %s' \
                  % (asset.ip, 'Auth failed')
            logger.warning(msg)
            failed = True

        except socket.error:
            msg = 'Connect asset %s failed: %s' % (asset.ip, 'Timeout')
            logger.warning(msg)
            failed = True
        else:
            msg = 'Connect asset %(username)s@%(host)s:%(port)s successfully' % {
                'username': system_user.username,
                'host': asset.ip,
                'port': asset.port
            }
            failed = False
            logger.info(msg)

        if failed:
            client_channel.send(wr(warning(msg + '\r\n')))
            data = {
                "proxy_log_id": proxy_log_id,
                "date_finished": time.time(),
                "was_failed": 1
            }
            self.service.finish_proxy_log(data)
            return None

        self.backend_channel = channel = ssh.invoke_shell(term=term,
                                                          width=width,
                                                          height=height)
        channel.settimeout(100)
        return channel
Ejemplo n.º 15
0
    def proxy(self):
        self.backend_channel = backend_channel = self.connect()
        client_channel = self.client_channel

        if backend_channel is None:
            return

        self.app.proxy_list[self.proxy_log_id] = \
            [self.client_channel, backend_channel]

        while not self.stop_event.set():
            try:
                r, w, x = select.select([client_channel, backend_channel], [],
                                        [])
            except select.error:
                break

            #if self.change_win_size_event.is_set():
            #    self.change_win_size_event.clear()
            #    width = self.client_channel.win_width
            #    height = self.client_channel.win_height
            #    backend_channel.resize_pty(width=width, height=height)

            if client_channel in r:
                # Get output of the command
                self.is_first_input = False
                if self.in_input_state is False:
                    self.get_output()
                    del self.output_data[:]

                self.in_input_state = True
                client_data = client_channel.recv(1024)

                if self.is_finish_input(client_data):
                    self.in_input_state = False
                    self.get_input()
                    del self.input_data[:]

                if len(client_data) == 0:
                    break
                backend_channel.send(client_data)

            if backend_channel in r:
                backend_data = backend_channel.recv(1024)
                if self.in_input_state:
                    self.input_data.append(backend_data)
                else:
                    self.output_data.append(backend_data)

                if len(backend_data) == 0:
                    client_channel.send(
                        wr('Disconnect from %s' % self.asset.ip))
                    logger.info('Logout from asset %(host)s: %(username)s' % {
                        'host': self.asset.ip,
                        'username': self.user.username,
                    })
                    break

                client_channel.send(backend_data)
                # Todo: record log send
                # if self.is_match_ignore_command(self.input):
                #     output = 'ignore output ...'
                # else:
                #     output = backend_data
                # record_data = {
                #     'proxy_log_id': self.proxy_log_id,
                #     'output': output,
                #     'timestamp': time.time(),
                # }
                # record_queue.put(record_data)

        data = {
            "proxy_log_id": self.proxy_log_id,
            "date_finished": time.time(),
        }
        self.service.finish_proxy_log(data)
        del self.app.proxy_list[self.proxy_log_id]
Ejemplo n.º 16
0
    def connect(self, term='xterm', width=80, height=24, timeout=10):
        user = self.user
        asset = self.asset
        system_user = self.system_user
        try:
            # Todo: win_width in request or client_channel
            width = int(self.client_channel.win_width)
            height = int(self.client_channel.win_height)
        except TypeError:
            pass
        if not self.validate_user_asset_permission():
            logger.warning('User %s have no permission connect %s with %s' %
                           (user.username, asset.ip, system_user.username))
            return None
        self.ssh = ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        password, private_key = self.get_asset_auth(system_user)

        data = {"user": user.username, "asset": asset.ip,
                "system_user": system_user.username,  "login_type": "ST",
                "date_start": time.time(), "is_failed": 0}
        self.proxy_log_id = proxy_log_id = self.service.send_proxy_log(data)
        self.app.proxy_list[proxy_log_id] = self.client_channel, self.backend_channel
        try:
            self.client_channel.send(
                wr('Connecting %s@%s:%s ... ' %
                   (system_user.username, asset.ip, asset.port)))
            ssh.connect(hostname=asset.ip, port=asset.port,
                        username=system_user.username,
                        password=password, pkey=private_key,
                        look_for_keys=False, allow_agent=True,
                        compress=True, timeout=timeout)

        except (paramiko.AuthenticationException,
                paramiko.ssh_exception.SSHException):
            msg = 'Connect backend server %s failed: %s' \
                  % (asset.ip, 'Auth failed')
            logger.warning(msg)
            failed = True

        except socket.error:
            msg = 'Connect asset %s failed: %s' % (asset.ip, 'Timeout')
            logger.warning(msg)
            failed = True
        else:
            msg = 'Connect asset %(username)s@%(host)s:%(port)s successfully' % {
                       'username': system_user.username,
                       'host': asset.ip,
                       'port': asset.port}
            failed = False
            logger.info(msg)

        if failed:
            self.client_channel.send(wr(warning(msg+'\r\n')))
            data = {
                "proxy_log_id": proxy_log_id,
                "date_finished": time.time(),
                "is_failed": 1
            }
            self.service.finish_proxy_log(data)
            return None

        self.backend_channel = channel = ssh.invoke_shell(
            term=term, width=width, height=height)
        channel.settimeout(100)
        return channel
Ejemplo n.º 17
0
    def proxy(self):
        self.backend_channel = self.connect()

        if self.backend_channel is None:
            return

        self.app.proxy_list[self.proxy_log_id] = \
            [self.client_channel, self.backend_channel]

        self.sel.register(self.client_channel, selectors.EVENT_READ)
        self.sel.register(self.backend_channel, selectors.EVENT_READ)

        while not self.stop_event.set():
            events = self.sel.select()  # block until data coming

            if self.change_win_size_event.is_set():
               self.change_win_size_event.clear()
               width = self.client_channel.win_width
               height = self.client_channel.win_height
               self.backend_channel.resize_pty(width=width, height=height)

            if self.client_channel in [t[0].fileobj for t in events]:
                # Get output of the command
                self.is_first_input = False
                if self.in_input_state is False:
                    self.get_output()
                    del self.output_data[:]

                self.in_input_state = True
                client_data = self.client_channel.recv(1024)

                if self.is_finish_input(client_data):
                    self.in_input_state = False
                    self.get_input()
                    del self.input_data[:]

                if len(client_data) == 0:
                    break
                self.backend_channel.send(client_data)

            if self.backend_channel in [t[0].fileobj for t in events]:
                backend_data = self.backend_channel.recv(1024)
                if self.in_input_state:
                    self.input_data.append(backend_data)
                else:
                    self.output_data.append(backend_data)

                if len(backend_data) == 0:
                    self.client_channel.send(
                        wr('Disconnect from %s' % self.asset.ip))
                    logger.info('Logout from asset %(host)s: %(username)s' % {
                        'host': self.asset.ip,
                        'username': self.user.username,
                    })
                    break

                self.client_channel.send(backend_data)
                # Todo: record log send
                # if self.is_match_ignore_command(self.input):
                #     output = 'ignore output ...'
                # else:
                #     output = backend_data
                # record_data = {
                #     'proxy_log_id': self.proxy_log_id,
                #     'output': output,
                #     'timestamp': time.time(),
                # }
                # record_queue.put(record_data)

        data = {
            "proxy_log_id": self.proxy_log_id,
            "date_finished": time.time(),
        }
        self.service.finish_proxy_log(data)
        del self.app.proxy_list[self.proxy_log_id]

        def close(self):
            self.backend_channel.close()
            sef.sel.close()
Ejemplo n.º 18
0
    def proxy(self):
        self.backend_channel = self.connect()

        if self.backend_channel is None:
            return

        self.app.proxy_list[self.proxy_log_id] = \
            [self.client_channel, self.backend_channel]

        self.sel.register(self.client_channel, selectors.EVENT_READ)
        self.sel.register(self.backend_channel, selectors.EVENT_READ)

        while not self.stop_event.set():
            events = self.sel.select()  # block until data coming

            if self.change_win_size_event.is_set():
                self.change_win_size_event.clear()
                width = self.client_channel.win_width
                height = self.client_channel.win_height
                self.backend_channel.resize_pty(width=width, height=height)

            if self.client_channel in [t[0].fileobj for t in events]:
                # Get output of the command
                self.is_first_input = False
                if self.in_input_state is False:
                    self.get_output()
                    del self.output_data[:]

                self.in_input_state = True
                client_data = self.client_channel.recv(1024)

                if self.is_finish_input(client_data):
                    self.in_input_state = False
                    self.get_input()
                    del self.input_data[:]

                if len(client_data) == 0:
                    break
                self.backend_channel.send(client_data)

            if self.backend_channel in [t[0].fileobj for t in events]:
                backend_data = self.backend_channel.recv(1024)
                if self.in_input_state:
                    self.input_data.append(backend_data)
                else:
                    self.output_data.append(backend_data)

                if len(backend_data) == 0:
                    self.client_channel.send(
                        wr('Disconnect from %s' % self.asset.ip))
                    logger.info('Logout from asset %(host)s: %(username)s' % {
                        'host': self.asset.ip,
                        'username': self.user.username,
                    })
                    break

                self.client_channel.send(backend_data)
                # Todo: record log send
                # if self.is_match_ignore_command(self.input):
                #     output = 'ignore output ...'
                # else:
                #     output = backend_data
                # record_data = {
                #     'proxy_log_id': self.proxy_log_id,
                #     'output': output,
                #     'timestamp': time.time(),
                # }
                # record_queue.put(record_data)

        data = {
            "proxy_log_id": self.proxy_log_id,
            "date_finished": time.time(),
        }
        self.service.finish_proxy_log(data)
        del self.app.proxy_list[self.proxy_log_id]

        def close(self):
            self.backend_channel.close()
            self.sel.close()