def _get_conn_info(self, param): if 'conn_id' not in param: return self.write_json(TPE_PARAM) conn_id = param['conn_id'] x = tp_session().taken('tmp-conn-info-{}'.format(conn_id), None) if x is None: return self.write_json(TPE_NOT_EXISTS) else: return self.write_json(TPE_OK, data=x)
def open(self, sid): # 处理新的连接 k = '{}-{}'.format('user', sid) _user = tp_session().get(k, None) if _user is None: ret = {'code': TPE_NEED_LOGIN, 'message': '需要登录'} self.write_message(json.dumps(ret)) return tp_wss().register(self)
def post(self): ret = self.check_privilege(TP_PRIVILEGE_USER_DELETE) if ret != TPE_OK: return args = self.get_argument('args', None) if args is None: return self.write_json(TPE_PARAM) try: args = json.loads(args) except: return self.write_json(TPE_JSON_FORMAT) try: action = args['action'] users = args['users'] except: return self.write_json(TPE_PARAM) if action == 'lock': err = user.update_users_state(self, users, TP_STATE_DISABLED) elif action == 'unlock': err = user.update_users_state(self, users, TP_STATE_NORMAL) elif action == 'remove': err = user.remove_users(self, users) else: err = TPE_PARAM if err != TPE_OK: return self.write_json(err) # force logout if user LOCKED or REMOVED. if action == 'lock' or action == 'remove': v = tp_session().get_start_with('user-') for k in v: if v[k]['v']['id'] in users: tp_session().taken(k) self.write_json(err)
def run(self): log.i('\n') log.i( '###############################################################\n' ) log.i('Load config file: {}\n'.format(self._cfg_file)) log.i('Teleport Web Server starting ...\n') # 尝试通过CORE-JSON-RPC获取core服务的配置(主要是ssh/rdp/telnet的端口以及录像文件存放路径) # self._get_core_server_config() _db = get_db() if not _db.init(): log.e('can not initialize database interface.\n') return 0 _db.connect() while not _db.connected: log.w('database not connected, retry after 5 seconds.\n') time.sleep(5) _db.connect() cfg = tp_cfg() _db.check_status() if _db.need_create or _db.need_upgrade: cfg.app_mode = APP_MODE_MAINTENANCE else: cfg.app_mode = APP_MODE_NORMAL if not tp_session().init(): log.e('can not initialize session manager.\n') return 0 # if not tp_stats().init(): # log.e('can not initialize system status collector.\n') # return 0 settings = { # 'cookie_secret': '8946svdABGD345fg98uhIaefEBePIfegOIakjFH43oETzK', 'login_url': '/auth/login', # 指定静态文件的路径,页面模板中可以用 {{ static_url('css/main.css') }} 的方式调用 'static_path': cfg.static_path, # 指定模板文件的路径 'template_path': cfg.template_path, # 防止跨站伪造请求,参见 http://old.sebug.net/paper/books/tornado/#_7 'xsrf_cookies': False, 'autoescape': 'xhtml_escape', # 'ui_modules': ui_modules, 'debug': False, # 不开启模板和静态文件的缓存,这样一旦模板文件和静态文件变化,刷新浏览器即可看到更新。 'compiled_template_cache': False, 'static_hash_cache': False, } from app.controller import controllers _app = tornado.web.Application(controllers, **settings) server = tornado.httpserver.HTTPServer(_app, xheaders=True) # server = tornado.httpserver.HTTPServer(_app, ssl_options={ # "certfile": os.path.join(cfg.data_path, 'cert', "server.pem"), # "keyfile": os.path.join(cfg.data_path, 'cert', "server.key"), # }) try: server.listen(cfg.common.port, address=cfg.common.ip) if cfg.common.ip == '0.0.0.0': log.i('works on [http://127.0.0.1:{}]\n'.format( cfg.common.port)) else: log.i('works on [http://{}:{}]\n'.format( cfg.common.ip, cfg.common.port)) except: log.e( 'can not listen on port {}:{}, make sure it not been used by another application.\n' .format(cfg.common.ip, cfg.common.port)) return 0 try: tornado.ioloop.IOLoop.instance().start() except: log.e('\n') return 0
def del_session(self, name): k = '{}-{}'.format(name, self._s_id) return tp_session().set(k, '', -1)
def get_session(self, name, _default=None): k = '{}-{}'.format(name, self._s_id) return tp_session().get(k, _default)
def set_session(self, name, value, expire=None): k = '{}-{}'.format(name, self._s_id) tp_session().set(k, value, expire)
def post(self): # ret = self.check_privilege(TP_PRIVILEGE_ASSET_CREATE | TP_PRIVILEGE_ASSET_DELETE | TP_PRIVILEGE_OPS | TP_PRIVILEGE_OPS_AUZ) # if ret != TPE_OK: # return args = self.get_argument('args', None) if args is None: return self.write_json(TPE_PARAM) try: args = json.loads(args) except: return self.write_json(TPE_JSON_FORMAT) # 有三种方式获取会话ID: # 1. 给定一个远程连接授权ID(普通用户进行远程连接) # 2. 给定要连接的主机ID和账号ID(管理员进行远程连接) # 3. 给定要连接的主机ID和账号信息(管理员测试远程连接是否可用) # # WEB服务根据上述信息产生临时的远程连接ID,核心服务通过此远程连接ID来获取远程连接所需数据,生成会话ID。 try: _mode = int(args['mode']) _protocol_type = int(args['protocol_type']) _protocol_sub_type = int(args['protocol_sub_type']) except: return self.write_json(TPE_PARAM) conn_info = dict() conn_info['_enc'] = 1 conn_info['host_id'] = 0 conn_info['client_ip'] = self.request.remote_ip conn_info['user_id'] = self.get_current_user()['id'] conn_info['user_username'] = self.get_current_user()['username'] # mode = 0: test connect # mode = 1: user connect # mode = 2: admin connect if _mode == 1: # 通过指定的auth_id连接(需要授权),必须具有远程运维的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_OPS) if ret != TPE_OK: return if 'auth_id' not in args or 'protocol_sub_type' not in args: return self.write_json(TPE_PARAM) # 根据auth_id从数据库中取得此授权相关的用户、主机、账号三者详细信息 auth_id = args['auth_id'] ops_auth, err = ops.get_auth(auth_id) if err != TPE_OK: return self.write_json(err) policy_id = ops_auth['p_id'] acc_id = ops_auth['a_id'] host_id = ops_auth['h_id'] err, policy_info = ops.get_by_id(policy_id) if err != TPE_OK: return self.write_json(err) err, acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) # log.v(acc_info) if acc_info['protocol_type'] == TP_PROTOCOL_TYPE_RDP: acc_info['protocol_flag'] = policy_info['flag_rdp'] elif acc_info['protocol_type'] == TP_PROTOCOL_TYPE_SSH: acc_info['protocol_flag'] = policy_info['flag_ssh'] elif acc_info['protocol_type'] == TP_PROTOCOL_TYPE_TELNET: acc_info['protocol_flag'] = policy_info['flag_telnet'] else: acc_info['protocol_flag'] = 0 acc_info['record_flag'] = policy_info['flag_record'] elif _mode == 2: # 直接连接(无需授权),必须具有运维授权管理的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_OPS_AUZ) if ret != TPE_OK: return acc_id = args['acc_id'] host_id = args['host_id'] err, acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) acc_info['protocol_flag'] = TP_FLAG_ALL acc_info['record_flag'] = TP_FLAG_ALL elif _mode == 0: # 测试连接,必须具有主机信息创建、编辑的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_ASSET_CREATE) if ret != TPE_OK: return conn_info['_test'] = 1 try: acc_id = int(args['acc_id']) host_id = int(args['host_id']) auth_type = int(args['auth_type']) username = args['username'] password = args['password'] pri_key = args['pri_key'] protocol_port = int(args['protocol_port']) username_prompt = args['username_prompt'] password_prompt = args['password_prompt'] except: return self.write_json(TPE_PARAM) if len(username) == 0: return self.write_json(TPE_PARAM) acc_info = dict() acc_info['auth_type'] = auth_type acc_info['protocol_type'] = _protocol_type acc_info['protocol_port'] = protocol_port acc_info['protocol_flag'] = TP_FLAG_ALL acc_info['record_flag'] = TP_FLAG_ALL acc_info['username'] = username acc_info['password'] = password acc_info['pri_key'] = pri_key acc_info['username_prompt'] = username_prompt acc_info['password_prompt'] = password_prompt conn_info['_enc'] = 0 if acc_id == -1: if auth_type == TP_AUTH_TYPE_PASSWORD and len(password) == 0: return self.write_json(TPE_PARAM) elif auth_type == TP_AUTH_TYPE_PRIVATE_KEY and len( pri_key) == 0: return self.write_json(TPE_PARAM) else: if (auth_type == TP_AUTH_TYPE_PASSWORD and len(password) == 0) or (auth_type == TP_AUTH_TYPE_PRIVATE_KEY and len(pri_key) == 0): err, _acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) acc_info['password'] = _acc_info['password'] acc_info['pri_key'] = _acc_info['pri_key'] conn_info['_enc'] = 1 else: return self.write_json(TPE_PARAM) # 获取要远程连接的主机信息(要访问的IP地址,如果是路由模式,则是路由主机的IP+端口) err, host_info = host.get_host_info(host_id) if err != TPE_OK: return self.write_json(err) conn_info['host_id'] = host_id conn_info['host_ip'] = host_info['ip'] if len(host_info['router_ip']) > 0: conn_info['conn_ip'] = host_info['router_ip'] conn_info['conn_port'] = host_info['router_port'] else: conn_info['conn_ip'] = host_info['ip'] conn_info['conn_port'] = acc_info['protocol_port'] conn_info['acc_id'] = acc_id conn_info['acc_username'] = acc_info['username'] conn_info['username_prompt'] = acc_info['username_prompt'] conn_info['password_prompt'] = acc_info['password_prompt'] conn_info['protocol_flag'] = acc_info['protocol_flag'] conn_info['record_flag'] = acc_info['record_flag'] conn_info['protocol_type'] = acc_info['protocol_type'] conn_info['protocol_sub_type'] = _protocol_sub_type conn_info['auth_type'] = acc_info['auth_type'] if acc_info['auth_type'] == TP_AUTH_TYPE_PASSWORD: conn_info['acc_secret'] = acc_info['password'] elif acc_info['auth_type'] == TP_AUTH_TYPE_PRIVATE_KEY: conn_info['acc_secret'] = acc_info['pri_key'] else: conn_info['acc_secret'] = '' with tmp_conn_id_lock: global tmp_conn_id_base tmp_conn_id_base += 1 conn_id = tmp_conn_id_base # log.v('CONN-INFO:', conn_info) tp_session().set('tmp-conn-info-{}'.format(conn_id), conn_info, 10) req = {'method': 'request_session', 'param': {'conn_id': conn_id}} _yr = core_service_async_post_http(req) _code, ret_data = yield _yr if _code != TPE_OK: return self.write_json(_code) if ret_data is None: return self.write_json(TPE_FAILED, '调用核心服务获取会话ID失败') if 'sid' not in ret_data: return self.write_json(TPE_FAILED, '核心服务获取会话ID时返回错误数据') data = dict() data['session_id'] = ret_data['sid'] data['host_ip'] = host_info['ip'] data['protocol_flag'] = acc_info['protocol_flag'] if conn_info['protocol_type'] == TP_PROTOCOL_TYPE_RDP: data['teleport_port'] = tp_cfg().core.rdp.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_SSH: data['teleport_port'] = tp_cfg().core.ssh.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_TELNET: data['teleport_port'] = tp_cfg().core.telnet.port return self.write_json(0, data=data)
def _run_loop(self): ext_srv_cfg = tp_ext_srv_cfg() if not ext_srv_cfg.init(): return 0 log.i('Teleport Web Server starting ...\n') tp_cron().init() # 尝试通过CORE-JSON-RPC获取core服务的配置(主要是ssh/rdp/telnet的端口以及录像文件存放路径) self._get_core_server_config() _db = get_db() if not _db.init(): log.e('can not initialize database interface.\n') return 0 _db.connect() while not _db.connected: log.w('database not connected, retry after 5 seconds.\n') time.sleep(5) _db.connect() cfg = tp_cfg() _db.check_status() if _db.need_create or _db.need_upgrade: cfg.app_mode = APP_MODE_MAINTENANCE tp_cfg().update_sys(None) else: cfg.app_mode = APP_MODE_NORMAL _db.load_system_config() try: # 将运行时配置发送给核心服务 req = {'method': 'set_config', 'param': {'noop_timeout': tp_cfg().sys.session.noop_timeout}} req_data = json.dumps(req) data = urllib.parse.quote(req_data).encode('utf-8') req = urllib.request.Request(url=cfg.common.core_server_rpc, data=data) rep = urllib.request.urlopen(req, timeout=3) body = rep.read().decode() x = json.loads(body) if 'code' not in x or x['code'] != 0: print(x) log.e('connect core-server for set runtime-config failed.\n') else: log.d('set runtime-config for core-server succeeded.\n') except: log.w('can not connect to core-server to set runtime-config, maybe it not start yet, ignore.\n') if not tp_session().init(): log.e('can not initialize session manager.\n') return 0 if not tp_stats().init(): log.e('can not initialize system status collector.\n') return 0 if cfg.common.check_host_alive: if not tp_host_alive().init(): log.e('can not initialize host state inspector.\n') return 0 settings = { # 'cookie_secret': '8946svdABGD345fg98uhIaefEBePIfegOIakjFH43oETzK', 'login_url': '/auth/login', # 指定静态文件的路径,页面模板中可以用 {{ static_url('css/main.css') }} 的方式调用 'static_path': cfg.static_path, # 指定模板文件的路径 'template_path': cfg.template_path, # 防止跨站伪造请求,参见 http://old.sebug.net/paper/books/tornado/#_7 'xsrf_cookies': False, 'autoescape': 'xhtml_escape', # 'ui_modules': ui_modules, 'debug': False, # 不开启模板和静态文件的缓存,这样一旦模板文件和静态文件变化,刷新浏览器即可看到更新。 'compiled_template_cache': False, 'static_hash_cache': False, } from app.controller import controllers, fix_controller fix_controller() _app = tornado.web.Application(controllers, **settings) server = tornado.httpserver.HTTPServer(_app, xheaders=True) # server = tornado.httpserver.HTTPServer(_app, xheaders=True, ssl_options={ # "certfile": os.path.join(cfg.data_path, 'cert', "server.pem"), # "keyfile": os.path.join(cfg.data_path, 'cert', "server.key"), # }) try: server.listen(cfg.common.port, address=cfg.common.ip) if cfg.common.ip == '0.0.0.0': log.i('works on [http://127.0.0.1:{}]\n'.format(cfg.common.port)) else: log.i('works on [http://{}:{}]\n'.format(cfg.common.ip, cfg.common.port)) except: log.e('can not listen on port {}:{}, make sure it not been used by another application.\n'.format(cfg.common.ip, cfg.common.port)) return 0 # 启动定时任务调度器 tp_cron().start() try: tornado.ioloop.IOLoop.instance().start() except: log.e('\n') if tp_cfg().common.check_host_alive: tp_host_alive().stop() tp_cron().stop() return 0
def post(self): ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG) if ret != TPE_OK: return args = self.get_argument('args', None) if args is None: return self.write_json(TPE_PARAM) try: args = json.loads(args) except: return self.write_json(TPE_JSON_FORMAT) try: processed = False if 'smtp' in args: processed = True _cfg = args['smtp'] _server = _cfg['server'] _port = _cfg['port'] _ssl = _cfg['ssl'] _sender = _cfg['sender'] _password = _cfg['password'] # TODO: encrypt the password before save by core-service. # TODO: if not send password, use pre-saved password. err = system_model.save_config(self, '更新SMTP设置', 'smtp', _cfg) if err == TPE_OK: # 同时更新内存缓存 tp_cfg().sys.smtp.server = _server tp_cfg().sys.smtp.port = _port tp_cfg().sys.smtp.ssl = _ssl tp_cfg().sys.smtp.sender = _sender # 特殊处理,防止前端拿到密码 tp_cfg().sys_smtp_password = _password else: return self.write_json(err) #增加urlprotocol的配置 if 'global' in args: processed = True _cfg = args['global'] _url_proto = _cfg['url_proto'] err = system_model.save_config(self, '更新全局设置', 'global', _cfg) if err == TPE_OK: tp_cfg().sys.glob.url_proto = _url_proto else: return self.write_json(err) if 'password' in args: processed = True _cfg = args['password'] _allow_reset = _cfg['allow_reset'] _force_strong = _cfg['force_strong'] _timeout = _cfg['timeout'] err = system_model.save_config(self, '更新密码策略设置', 'password', _cfg) if err == TPE_OK: tp_cfg().sys.password.allow_reset = _allow_reset tp_cfg().sys.password.force_strong = _force_strong tp_cfg().sys.password.timeout = _timeout else: return self.write_json(err) if 'login' in args: processed = True _cfg = args['login'] _session_timeout = _cfg['session_timeout'] _retry = _cfg['retry'] _lock_timeout = _cfg['lock_timeout'] _auth = _cfg['auth'] err = system_model.save_config(self, '更新登录策略设置', 'login', _cfg) if err == TPE_OK: tp_cfg().sys.login.session_timeout = _session_timeout tp_cfg().sys.login.retry = _retry tp_cfg().sys.login.lock_timeout = _lock_timeout tp_cfg().sys.login.auth = _auth tp_session().update_default_expire() else: return self.write_json(err) if 'session' in args: processed = True _cfg = args['session'] _noop_timeout = _cfg['noop_timeout'] _flag_record = _cfg['flag_record'] _flag_rdp = _cfg['flag_rdp'] _flag_ssh = _cfg['flag_ssh'] err = system_model.save_config(self, '更新连接控制设置', 'session', _cfg) if err == TPE_OK: try: req = {'method': 'set_config', 'param': {'noop_timeout': _noop_timeout}} _yr = core_service_async_post_http(req) code, ret_data = yield _yr if code != TPE_OK: log.e('can not set runtime-config to core-server.\n') return self.write_json(code) except: pass tp_cfg().sys.session.noop_timeout = _noop_timeout tp_cfg().sys.session.flag_record = _flag_record tp_cfg().sys.session.flag_rdp = _flag_rdp tp_cfg().sys.session.flag_ssh = _flag_ssh else: return self.write_json(err) if 'storage' in args: processed = True _cfg = args['storage'] _keep_log = _cfg['keep_log'] _keep_record = _cfg['keep_record'] _cleanup_hour = _cfg['cleanup_hour'] _cleanup_minute = _cfg['cleanup_minute'] if not ((30 <= _keep_log <= 365) or _keep_log == 0): return self.write_json(TPE_PARAM, '系统日志保留时间超出范围!') if not ((30 <= _keep_record <= 365) or _keep_record == 0): return self.write_json(TPE_PARAM, '会话录像保留时间超出范围!') err = system_model.save_config(self, '更新存储策略设置', 'storage', _cfg) if err == TPE_OK: tp_cfg().sys.storage.keep_log = _keep_log tp_cfg().sys.storage.keep_record = _keep_record tp_cfg().sys.storage.cleanup_hour = _cleanup_hour tp_cfg().sys.storage.cleanup_minute = _cleanup_minute else: return self.write_json(err) if 'ldap' in args: processed = True _cfg = args['ldap'] # _password = _cfg['password'] _server = _cfg['server'] _port = _cfg['port'] _domain = _cfg['domain'] _admin = _cfg['admin'] _base_dn = _cfg['base_dn'] _filter = _cfg['filter'] _attr_username = _cfg['attr_username'] _attr_surname = _cfg['attr_surname'] _attr_email = _cfg['attr_email'] if len(_cfg['password']) == 0: _cfg['password'] = tp_cfg().sys_ldap_password if len(_cfg['password']) == 0: return self.write_json(TPE_PARAM, '请设置LDAP管理员密码') # TODO: encrypt the password before save by core-service. err = system_model.save_config(self, '更新LDAP设置', 'ldap', _cfg) if err == TPE_OK: tp_cfg().sys.ldap.server = _server tp_cfg().sys.ldap.port = _port tp_cfg().sys.ldap.domain = _domain tp_cfg().sys.ldap.admin = _admin tp_cfg().sys.ldap.base_dn = _base_dn tp_cfg().sys.ldap.filter = _filter tp_cfg().sys.ldap.attr_username = _attr_username tp_cfg().sys.ldap.attr_surname = _attr_surname tp_cfg().sys.ldap.attr_email = _attr_email # 特殊处理,防止前端拿到密码 tp_cfg().sys_ldap_password = _cfg['password'] else: return self.write_json(err) if not processed: return self.write_json(TPE_PARAM) return self.write_json(TPE_OK) except: log.e('\n') self.write_json(TPE_FAILED)
def post(self): # ret = self.check_privilege(TP_PRIVILEGE_ASSET_CREATE | TP_PRIVILEGE_ASSET_DELETE | TP_PRIVILEGE_OPS | TP_PRIVILEGE_OPS_AUZ) # if ret != TPE_OK: # return args = self.get_argument('args', None) if args is None: return self.write_json(TPE_PARAM) try: args = json.loads(args) except: return self.write_json(TPE_JSON_FORMAT) # 有三种方式获取会话ID: # 1. 给定一个远程连接授权ID(普通用户进行远程连接) # 2. 给定要连接的主机ID和账号ID(管理员进行远程连接) # 3. 给定要连接的主机ID和账号信息(管理员测试远程连接是否可用) # # WEB服务根据上述信息产生临时的远程连接ID,核心服务通过此远程连接ID来获取远程连接所需数据,生成会话ID。 try: _mode = int(args['mode']) _protocol_type = int(args['protocol_type']) _protocol_sub_type = int(args['protocol_sub_type']) except: return self.write_json(TPE_PARAM) conn_info = dict() conn_info['_enc'] = 1 conn_info['host_id'] = 0 conn_info['client_ip'] = self.request.remote_ip conn_info['user_id'] = self.get_current_user()['id'] conn_info['user_username'] = self.get_current_user()['username'] # mode = 0: test connect # mode = 1: user connect # mode = 2: admin connect if _mode == 1: # 通过指定的auth_id连接(需要授权),必须具有远程运维的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_OPS) if ret != TPE_OK: return if 'auth_id' not in args or 'protocol_sub_type' not in args: return self.write_json(TPE_PARAM) # 根据auth_id从数据库中取得此授权相关的用户、主机、账号三者详细信息 auth_id = args['auth_id'] ops_auth, err = ops.get_auth(auth_id) if err != TPE_OK: return self.write_json(err) policy_id = ops_auth['p_id'] acc_id = ops_auth['a_id'] host_id = ops_auth['h_id'] err, policy_info = ops.get_by_id(policy_id) if err != TPE_OK: return self.write_json(err) err, acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) # log.v(acc_info) if acc_info['protocol_type'] == TP_PROTOCOL_TYPE_RDP: acc_info['protocol_flag'] = policy_info['flag_rdp'] elif acc_info['protocol_type'] == TP_PROTOCOL_TYPE_SSH: acc_info['protocol_flag'] = policy_info['flag_ssh'] elif acc_info['protocol_type'] == TP_PROTOCOL_TYPE_TELNET: acc_info['protocol_flag'] = policy_info['flag_telnet'] else: acc_info['protocol_flag'] = 0 acc_info['record_flag'] = policy_info['flag_record'] elif _mode == 2: # 直接连接(无需授权),必须具有运维授权管理的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_OPS_AUZ) if ret != TPE_OK: return acc_id = args['acc_id'] host_id = args['host_id'] err, acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) acc_info['protocol_flag'] = TP_FLAG_ALL acc_info['record_flag'] = TP_FLAG_ALL elif _mode == 0: # 测试连接,必须具有主机信息创建、编辑的权限方可进行 ret = self.check_privilege(TP_PRIVILEGE_ASSET_CREATE) if ret != TPE_OK: return conn_info['_test'] = 1 try: acc_id = int(args['acc_id']) host_id = int(args['host_id']) auth_type = int(args['auth_type']) username = args['username'] password = args['password'] pri_key = args['pri_key'] protocol_port = int(args['protocol_port']) username_prompt = args['username_prompt'] password_prompt = args['password_prompt'] except: return self.write_json(TPE_PARAM) if len(username) == 0: return self.write_json(TPE_PARAM) acc_info = dict() acc_info['auth_type'] = auth_type acc_info['protocol_type'] = _protocol_type acc_info['protocol_port'] = protocol_port acc_info['protocol_flag'] = TP_FLAG_ALL acc_info['record_flag'] = TP_FLAG_ALL acc_info['username'] = username acc_info['password'] = password acc_info['pri_key'] = pri_key acc_info['username_prompt'] = username_prompt acc_info['password_prompt'] = password_prompt conn_info['_enc'] = 0 if acc_id == -1: if auth_type == TP_AUTH_TYPE_PASSWORD and len(password) == 0: return self.write_json(TPE_PARAM) elif auth_type == TP_AUTH_TYPE_PRIVATE_KEY and len(pri_key) == 0: return self.write_json(TPE_PARAM) else: if (auth_type == TP_AUTH_TYPE_PASSWORD and len(password) == 0) or (auth_type == TP_AUTH_TYPE_PRIVATE_KEY and len(pri_key) == 0): err, _acc_info = account.get_account_info(acc_id) if err != TPE_OK: return self.write_json(err) acc_info['password'] = _acc_info['password'] acc_info['pri_key'] = _acc_info['pri_key'] conn_info['_enc'] = 1 else: return self.write_json(TPE_PARAM) # 获取要远程连接的主机信息(要访问的IP地址,如果是路由模式,则是路由主机的IP+端口) err, host_info = host.get_host_info(host_id) if err != TPE_OK: return self.write_json(err) conn_info['host_id'] = host_id conn_info['host_ip'] = host_info['ip'] if len(host_info['router_ip']) > 0: conn_info['conn_ip'] = host_info['router_ip'] conn_info['conn_port'] = host_info['router_port'] else: conn_info['conn_ip'] = host_info['ip'] conn_info['conn_port'] = acc_info['protocol_port'] conn_info['acc_id'] = acc_id conn_info['acc_username'] = acc_info['username'] conn_info['username_prompt'] = acc_info['username_prompt'] conn_info['password_prompt'] = acc_info['password_prompt'] conn_info['protocol_flag'] = acc_info['protocol_flag'] conn_info['record_flag'] = acc_info['record_flag'] conn_info['protocol_type'] = acc_info['protocol_type'] conn_info['protocol_sub_type'] = _protocol_sub_type conn_info['auth_type'] = acc_info['auth_type'] if acc_info['auth_type'] == TP_AUTH_TYPE_PASSWORD: conn_info['acc_secret'] = acc_info['password'] elif acc_info['auth_type'] == TP_AUTH_TYPE_PRIVATE_KEY: conn_info['acc_secret'] = acc_info['pri_key'] else: conn_info['acc_secret'] = '' with tmp_conn_id_lock: global tmp_conn_id_base tmp_conn_id_base += 1 conn_id = tmp_conn_id_base # log.v('CONN-INFO:', conn_info) tp_session().set('tmp-conn-info-{}'.format(conn_id), conn_info, 10) req = {'method': 'request_session', 'param': {'conn_id': conn_id}} _yr = core_service_async_post_http(req) _code, ret_data = yield _yr if _code != TPE_OK: return self.write_json(_code) if ret_data is None: return self.write_json(TPE_FAILED, '调用核心服务获取会话ID失败') if 'sid' not in ret_data: return self.write_json(TPE_FAILED, '核心服务获取会话ID时返回错误数据') data = dict() data['session_id'] = ret_data['sid'] data['host_ip'] = host_info['ip'] data['protocol_flag'] = acc_info['protocol_flag'] if conn_info['protocol_type'] == TP_PROTOCOL_TYPE_RDP: data['teleport_port'] = tp_cfg().core.rdp.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_SSH: data['teleport_port'] = tp_cfg().core.ssh.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_TELNET: data['teleport_port'] = tp_cfg().core.telnet.port return self.write_json(0, data=data)
def run(self): log.i('\n') log.i('###############################################################\n') log.i('Load config file: {}\n'.format(self._cfg_file)) log.i('Teleport Web Server starting ...\n') tp_cron().init() # 尝试通过CORE-JSON-RPC获取core服务的配置(主要是ssh/rdp/telnet的端口以及录像文件存放路径) self._get_core_server_config() _db = get_db() if not _db.init(): log.e('can not initialize database interface.\n') return 0 _db.connect() while not _db.connected: log.w('database not connected, retry after 5 seconds.\n') time.sleep(5) _db.connect() cfg = tp_cfg() _db.check_status() if _db.need_create or _db.need_upgrade: cfg.app_mode = APP_MODE_MAINTENANCE tp_cfg().update_sys(None) else: cfg.app_mode = APP_MODE_NORMAL _db.load_system_config() try: # 将运行时配置发送给核心服务 req = {'method': 'set_config', 'param': {'noop_timeout': tp_cfg().sys.session.noop_timeout}} req_data = json.dumps(req) data = urllib.parse.quote(req_data).encode('utf-8') req = urllib.request.Request(url=cfg.common.core_server_rpc, data=data) rep = urllib.request.urlopen(req, timeout=3) body = rep.read().decode() x = json.loads(body) if 'code' not in x or x['code'] != 0: print(x) log.e('connect core-server for set runtime-config failed.\n') else: log.d('set runtime-config for core-server succeeded.\n') except: log.w('can not connect to core-server to set runtime-config, maybe it not start yet, ignore.\n') if not tp_session().init(): log.e('can not initialize session manager.\n') return 0 if not tp_stats().init(): log.e('can not initialize system status collector.\n') return 0 settings = { # 'cookie_secret': '8946svdABGD345fg98uhIaefEBePIfegOIakjFH43oETzK', 'login_url': '/auth/login', # 指定静态文件的路径,页面模板中可以用 {{ static_url('css/main.css') }} 的方式调用 'static_path': cfg.static_path, # 指定模板文件的路径 'template_path': cfg.template_path, # 防止跨站伪造请求,参见 http://old.sebug.net/paper/books/tornado/#_7 'xsrf_cookies': False, 'autoescape': 'xhtml_escape', # 'ui_modules': ui_modules, 'debug': False, # 不开启模板和静态文件的缓存,这样一旦模板文件和静态文件变化,刷新浏览器即可看到更新。 'compiled_template_cache': False, 'static_hash_cache': False, } from app.controller import controllers, fix_controller fix_controller() _app = tornado.web.Application(controllers, **settings) server = tornado.httpserver.HTTPServer(_app, xheaders=True) # server = tornado.httpserver.HTTPServer(_app, ssl_options={ # "certfile": os.path.join(cfg.data_path, 'cert', "server.pem"), # "keyfile": os.path.join(cfg.data_path, 'cert', "server.key"), # }) try: server.listen(cfg.common.port, address=cfg.common.ip) if cfg.common.ip == '0.0.0.0': log.i('works on [http://127.0.0.1:{}]\n'.format(cfg.common.port)) else: log.i('works on [http://{}:{}]\n'.format(cfg.common.ip, cfg.common.port)) except: log.e('can not listen on port {}:{}, make sure it not been used by another application.\n'.format(cfg.common.ip, cfg.common.port)) return 0 # 启动定时任务调度器 tp_cron().start() try: tornado.ioloop.IOLoop.instance().start() except: log.e('\n') tp_cron().stop() return 0
def api_request_session_id(acc_id, protocol_sub_type, client_ip, operator): ret = {'code': TPE_OK, 'message': '', 'data': {}} conn_info = dict() conn_info['_enc'] = 1 conn_info['host_id'] = 0 conn_info['client_ip'] = client_ip conn_info['user_id'] = 1 conn_info['user_username'] = operator # 直接连接(无需授权,第三方服务操作,已经经过授权检查了) err, acc_info = account.get_account_info(acc_id) if err != TPE_OK: ret['code'] = err ret['message'] = '无此远程账号' return ret host_id = acc_info['host_id'] acc_info['protocol_flag'] = TP_FLAG_ALL acc_info['record_flag'] = TP_FLAG_ALL # 获取要远程连接的主机信息(要访问的IP地址,如果是路由模式,则是路由主机的IP+端口) err, host_info = host.get_host_info(host_id) if err != TPE_OK: ret['code'] = err ret['message'] = '未找到对应远程主机' return ret conn_info['host_id'] = host_id conn_info['host_ip'] = host_info['ip'] if len(host_info['router_ip']) > 0: conn_info['conn_ip'] = host_info['router_ip'] conn_info['conn_port'] = host_info['router_port'] else: conn_info['conn_ip'] = host_info['ip'] conn_info['conn_port'] = acc_info['protocol_port'] conn_info['acc_id'] = acc_id conn_info['acc_username'] = acc_info['username'] conn_info['username_prompt'] = acc_info['username_prompt'] conn_info['password_prompt'] = acc_info['password_prompt'] conn_info['protocol_flag'] = acc_info['protocol_flag'] conn_info['record_flag'] = acc_info['record_flag'] conn_info['protocol_type'] = acc_info['protocol_type'] conn_info['protocol_sub_type'] = protocol_sub_type conn_info['auth_type'] = acc_info['auth_type'] if acc_info['auth_type'] == TP_AUTH_TYPE_PASSWORD: conn_info['acc_secret'] = acc_info['password'] elif acc_info['auth_type'] == TP_AUTH_TYPE_PRIVATE_KEY: conn_info['acc_secret'] = acc_info['pri_key'] else: conn_info['acc_secret'] = '' with tmp_conn_id_lock: global tmp_conn_id_base tmp_conn_id_base += 1 conn_id = tmp_conn_id_base # log.v('CONN-INFO:', conn_info) tp_session().set('tmp-conn-info-{}'.format(conn_id), conn_info, 10) req = {'method': 'request_session', 'param': {'conn_id': conn_id}} _yr = core_service_async_post_http(req) _code, ret_data = yield _yr if _code != TPE_OK: ret['code'] = _code ret['message'] = '无法连接到核心服务' return ret if ret_data is None: ret['code'] = TPE_FAILED ret['message'] = '调用核心服务获取会话ID失败' return ret if 'sid' not in ret_data: ret['code'] = TPE_FAILED ret['message'] = '核心服务获取会话ID时返回错误数据' return ret data = dict() data['session_id'] = ret_data['sid'] data['host_ip'] = host_info['ip'] data['host_name'] = host_info['name'] data['protocol_flag'] = acc_info['protocol_flag'] if conn_info['protocol_type'] == TP_PROTOCOL_TYPE_RDP: data['teleport_port'] = tp_cfg().core.rdp.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_SSH: data['teleport_port'] = tp_cfg().core.ssh.port elif conn_info['protocol_type'] == TP_PROTOCOL_TYPE_TELNET: data['teleport_port'] = tp_cfg().core.telnet.port ret['code'] = TPE_OK ret['message'] = '' ret['data'] = data return ret
def post(self): ret = self.check_privilege(TP_PRIVILEGE_SYS_CONFIG) if ret != TPE_OK: return args = self.get_argument('args', None) if args is None: return self.write_json(TPE_PARAM) try: args = json.loads(args) except: return self.write_json(TPE_JSON_FORMAT) try: processed = False if 'smtp' in args: processed = True _cfg = args['smtp'] _server = _cfg['server'] _port = _cfg['port'] _ssl = _cfg['ssl'] _sender = _cfg['sender'] _password = _cfg['password'] err = system_model.save_config(self, '更新SMTP设置', 'smtp', _cfg) if err == TPE_OK: # 同时更新内存缓存 tp_cfg().sys.smtp.server = _server tp_cfg().sys.smtp.port = _port tp_cfg().sys.smtp.ssl = _ssl tp_cfg().sys.smtp.sender = _sender # 特殊处理,防止前端拿到密码 tp_cfg().sys_smtp_password = _password else: return self.write_json(err) if 'password' in args: processed = True _cfg = args['password'] _allow_reset = _cfg['allow_reset'] _force_strong = _cfg['force_strong'] _timeout = _cfg['timeout'] err = system_model.save_config(self, '更新密码策略设置', 'password', _cfg) if err == TPE_OK: tp_cfg().sys.password.allow_reset = _allow_reset tp_cfg().sys.password.force_strong = _force_strong tp_cfg().sys.password.timeout = _timeout else: return self.write_json(err) if 'login' in args: processed = True _cfg = args['login'] _session_timeout = _cfg['session_timeout'] _retry = _cfg['retry'] _lock_timeout = _cfg['lock_timeout'] _auth = _cfg['auth'] err = system_model.save_config(self, '更新登录策略设置', 'login', _cfg) if err == TPE_OK: tp_cfg().sys.login.session_timeout = _session_timeout tp_cfg().sys.login.retry = _retry tp_cfg().sys.login.lock_timeout = _lock_timeout tp_cfg().sys.login.auth = _auth tp_session().update_default_expire() else: return self.write_json(err) if 'session' in args: processed = True _cfg = args['session'] _noop_timeout = _cfg['noop_timeout'] _flag_record = _cfg['flag_record'] _flag_rdp = _cfg['flag_rdp'] _flag_ssh = _cfg['flag_ssh'] err = system_model.save_config(self, '更新连接控制设置', 'session', _cfg) if err == TPE_OK: try: req = { 'method': 'set_config', 'param': { 'noop_timeout': _noop_timeout } } _yr = core_service_async_post_http(req) code, ret_data = yield _yr if code != TPE_OK: log.e( 'can not set runtime-config to core-server.\n') return self.write_json(code) except: pass tp_cfg().sys.session.noop_timeout = _noop_timeout tp_cfg().sys.session.flag_record = _flag_record tp_cfg().sys.session.flag_rdp = _flag_rdp tp_cfg().sys.session.flag_ssh = _flag_ssh else: return self.write_json(err) if 'storage' in args: processed = True _cfg = args['storage'] _keep_log = _cfg['keep_log'] _keep_record = _cfg['keep_record'] _cleanup_hour = _cfg['cleanup_hour'] _cleanup_minute = _cfg['cleanup_minute'] if not ((30 <= _keep_log <= 365) or _keep_log == 0): return self.write_json(TPE_PARAM, '系统日志保留时间超出范围!') if not ((30 <= _keep_record <= 365) or _keep_record == 0): return self.write_json(TPE_PARAM, '会话录像保留时间超出范围!') err = system_model.save_config(self, '更新存储策略设置', 'storage', _cfg) if err == TPE_OK: tp_cfg().sys.storage.keep_log = _keep_log tp_cfg().sys.storage.keep_record = _keep_record tp_cfg().sys.storage.cleanup_hour = _cleanup_hour tp_cfg().sys.storage.cleanup_minute = _cleanup_minute else: return self.write_json(err) if not processed: return self.write_json(TPE_PARAM) return self.write_json(TPE_OK) except: log.e('\n') self.write_json(TPE_FAILED)