diff --git a/connect.py b/connect.py index 46b1da82b..b2e941472 100644 --- a/connect.py +++ b/connect.py @@ -25,10 +25,9 @@ from multiprocessing import Pool os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from juser.models import User from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, JassetGroup -from jumpserver.api import AssetAlias, get_connect_item, logger +from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, Jasset, JassetGroup +from jumpserver.api import CRYPTOR, logger, is_dir try: import termios @@ -63,6 +62,8 @@ class Jtty(object): self.chan = chan self.username = user.username self.ip = asset.ip + self.user = user + self.asset = asset @staticmethod def get_win_size(): @@ -99,58 +100,51 @@ class Jtty(object): date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_filename = '%s_%s_%s.log' % (self.username, self.host, time_start) + log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept = User.objects.filter(username=username) - if dept: - dept = dept[0] - dept_name = dept.name - else: - dept_name = 'None' + dept_name = self.user.dept.name pid = os.getpid() pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - if not os.path.isdir(today_connect_log_dir): - try: - os.makedirs(today_connect_log_dir) - os.chmod(today_connect_log_dir, 0777) - except OSError: - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) + try: + is_dir(today_connect_log_dir) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: log_file = open(log_file_path, 'a') except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=username, host=host, remote_ip=remote_ip, dept_name=dept_name, + log = Log(user=self.username, host=self.ip, remote_ip=remote_ip, dept_name=dept_name, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) log_file.write('Start time is %s\n' % datetime.datetime.now()) log.save() return log_file, log - def posix_shell(chan, username, host): + def posix_shell(self): """ Use paramiko channel connect server interactive. 使用paramiko模块的channel,连接后端,进入交互式 """ - log_file, log = log_record(username, host) + log_file, log = self.log_record() old_tty = termios.tcgetattr(sys.stdin) try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) - chan.settimeout(0.0) + self.chan.settimeout(0.0) while True: try: - r, w, e = select.select([chan, sys.stdin], [], []) + r, w, e = select.select([self.chan, sys.stdin], [], []) except Exception: pass - if chan in r: + if self.chan in r: try: - x = chan.recv(1024) + x = self.chan.recv(1024) if len(x) == 0: break sys.stdout.write(x) @@ -164,7 +158,7 @@ class Jtty(object): x = os.read(sys.stdin.fileno(), 1) if len(x) == 0: break - chan.send(x) + self.chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) @@ -175,8 +169,72 @@ class Jtty(object): log.end_time = datetime.datetime.now() log.save() + def get_connect_item(self): + port = int(self.asset.port) -def verify_connect(username, part_ip): + if not self.asset.is_active: + raise ServerError('Host %s is not active.' % self.ip) + + if not self.user.is_active: + raise ServerError('User %s is not active.' % self.username) + + login_type_dict = { + 'L': self.user.ldap_pwd, + } + + if self.asset.login_type in login_type_dict: + password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) + return self.username, password, self.ip, port + + elif self.asset.login_type == 'M': + username = self.asset.username + password = CRYPTOR.decrypt(self.asset.password) + return username, password, self.ip, port + + else: + raise ServerError('Login type is not in ["L", "M"]') + + def connect(self): + """ + Connect server. + """ + username, password, ip, port = self.get_connect_item() + ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip + + # Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(ip, port=port, username=username, password=password, compress=True) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('Authentication Error.') + except socket.error: + raise ServerError('Connect SSH Socket Port Error, Please Correct it.') + + # Make a channel and set windows size + global channel + win_size = self.get_win_size() + channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, self.set_win_size) + except: + pass + + # Set PS1 and msg it + channel.send(ps1) + channel.send(login_msg) + + # Make ssh interactive tunnel + self.posix_shell() + + # Shutdown channel socket + channel.close() + ssh.close() + + +def verify_connect(user, option): ip_matched = [] try: assets_info = login_user.get_asset_info() @@ -185,15 +243,15 @@ def verify_connect(username, part_ip): return False for ip, asset_info in assets_info.items(): - if part_ip in asset_info[1:] and part_ip: + if option in asset_info[1:] and option: ip_matched = [asset_info[1]] break for info in asset_info[1:]: - if part_ip in info: + if option in info: ip_matched.append(ip) - logger.debug('%s matched input %s: %s' % (login_user.username, part_ip, ip_matched)) + logger.debug('%s matched input %s: %s' % (login_user.username, option, ip_matched)) ip_matched = list(set(ip_matched)) if len(ip_matched) > 1: @@ -206,8 +264,8 @@ def verify_connect(username, part_ip): elif len(ip_matched) < 1: color_print('No Permission or No host.', 'red') else: - username, password, host, port = get_connect_item(username, ip_matched[0]) - connect(username, password, host, port, login_name) + asset = Jasset(ip=ip_matched[0]) + jtty = Jtty(chan, user, ) def print_prompt(): @@ -242,113 +300,74 @@ def print_prompt(): # color_print('No such group id, Please check it.', 'red') -def connect(username, password, host, port, login_name): - """ - Connect server. - """ - ps1 = "PS1='[\u@%s \W]\$ '\n" % host - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % host - - # Make a ssh connection - ssh = paramiko.SSHClient() - ssh.load_system_host_keys() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(host, port=port, username=username, password=password, compress=True) - except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: - raise ServerError('Authentication Error.') - except socket.error: - raise ServerError('Connect SSH Socket Port Error, Please Correct it.') - - # Make a channel and set windows size - global channel - win_size = get_win_size() - channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) - try: - signal.signal(signal.SIGWINCH, set_win_size) - except: - pass - - # Set PS1 and msg it - channel.send(ps1) - channel.send(login_msg) - - # Make ssh interactive tunnel - posix_shell(channel, login_name, host) - - # Shutdown channel socket - channel.close() - ssh.close() +# def remote_exec_cmd(ip, port, username, password, cmd): +# try: +# time.sleep(5) +# ssh = paramiko.SSHClient() +# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +# ssh.connect(ip, port, username, password, timeout=5) +# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) +# out = stdout.readlines() +# err = stderr.readlines() +# color_print('%s:' % ip, 'blue') +# for i in out: +# color_print(" " * 4 + i.strip(), 'green') +# for j in err: +# color_print(" " * 4 + j.strip(), 'red') +# ssh.close() +# except Exception as e: +# color_print(ip + ':', 'blue') +# color_print(str(e), 'red') -def remote_exec_cmd(ip, port, username, password, cmd): - try: - time.sleep(5) - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(ip, port, username, password, timeout=5) - stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) - out = stdout.readlines() - err = stderr.readlines() - color_print('%s:' % ip, 'blue') - for i in out: - color_print(" " * 4 + i.strip(), 'green') - for j in err: - color_print(" " * 4 + j.strip(), 'red') - ssh.close() - except Exception as e: - color_print(ip + ':', 'blue') - color_print(str(e), 'red') +# def multi_remote_exec_cmd(hosts, username, cmd): +# pool = Pool(processes=5) +# for host in hosts: +# username, password, ip, port = get_connect_item(username, host) +# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) +# pool.close() +# pool.join() -def multi_remote_exec_cmd(hosts, username, cmd): - pool = Pool(processes=5) - for host in hosts: - username, password, ip, port = get_connect_item(username, host) - pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) - pool.close() - pool.join() - - -def exec_cmd_servers(username): - color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') - user.get_asset_info(printable=True) - while True: - hosts = [] - inputs = raw_input('\033[1;32mip(s)>: \033[0m') - if inputs in ['q', 'Q']: - break - get_hosts = login_user.get_asset_info().keys() - - if ',' in inputs: - ips_input = inputs.split(',') - for host in ips_input: - if host in get_hosts: - hosts.append(host) - else: - for host in get_hosts: - if fnmatch.fnmatch(host, inputs): - hosts.append(host.strip()) - - if len(hosts) == 0: - color_print("Check again, Not matched any ip!", 'red') - continue - else: - print "You matched ip: %s" % hosts - color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') - while True: - cmd = raw_input('\033[1;32mCmd(s): \033[0m') - if cmd in ['q', 'Q']: - break - exec_log_dir = os.path.join(log_dir, 'exec_cmds') - if not os.path.isdir(exec_log_dir): - os.mkdir(exec_log_dir) - os.chmod(exec_log_dir, 0777) - filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) - f = open(filename, 'a') - f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % - (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) - multi_remote_exec_cmd(hosts, username, cmd) +# def exec_cmd_servers(username): +# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') +# user.get_asset_info(printable=True) +# while True: +# hosts = [] +# inputs = raw_input('\033[1;32mip(s)>: \033[0m') +# if inputs in ['q', 'Q']: +# break +# get_hosts = login_user.get_asset_info().keys() +# +# if ',' in inputs: +# ips_input = inputs.split(',') +# for host in ips_input: +# if host in get_hosts: +# hosts.append(host) +# else: +# for host in get_hosts: +# if fnmatch.fnmatch(host, inputs): +# hosts.append(host.strip()) +# +# if len(hosts) == 0: +# color_print("Check again, Not matched any ip!", 'red') +# continue +# else: +# print "You matched ip: %s" % hosts +# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') +# while True: +# cmd = raw_input('\033[1;32mCmd(s): \033[0m') +# if cmd in ['q', 'Q']: +# break +# exec_log_dir = os.path.join(log_dir, 'exec_cmds') +# if not os.path.isdir(exec_log_dir): +# os.mkdir(exec_log_dir) +# os.chmod(exec_log_dir, 0777) +# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) +# f = open(filename, 'a') +# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % +# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) +# multi_remote_exec_cmd(hosts, username, cmd) if __name__ == '__main__': @@ -379,12 +398,13 @@ if __name__ == '__main__': asset_group.get_asset_info(printable=True) continue elif option in ['E', 'e']: - exec_cmd_servers(login_name) + # exec_cmd_servers(login_name) + pass elif option in ['Q', 'q', 'exit']: sys.exit() else: try: - verify_connect(login_name, option) + verify_connect(login_user, option) except ServerError, e: color_print(e, 'red') except IndexError: diff --git a/jumpserver/api.py b/jumpserver/api.py index 68831bcb0..5066bca8c 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -101,20 +101,6 @@ class LDAPMgmt(): print e -if LDAP_ENABLE: - LDAP_HOST_URL = CONF.get('ldap', 'host_url') - LDAP_BASE_DN = CONF.get('ldap', 'base_dn') - LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') - LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) -else: - ldap_conn = None - - -def md5_crypt(string): - return hashlib.new("md5", string).hexdigest() - - def page_list_return(total, current=1): min_page = current - 2 if current - 4 > 0 else 1 max_page = min_page + 4 if min_page + 4 < total else total @@ -160,7 +146,8 @@ class PyCrypt(object): self.key = key self.mode = AES.MODE_CBC - def _random_pass(self): + @staticmethod + def random_pass(): """ random password 随机生成密码 @@ -173,23 +160,30 @@ class PyCrypt(object): for i in range(4): salt_list.append(random.choice(symbol)) salt = ''.join(salt_list) - self.salt = salt + return salt - def encrypt(self): + @staticmethod + def md5_crypt(string): + return hashlib.new("md5", string).hexdigest() + + def encrypt(self, passwd=None): """ encrypt gen password 加密生成密码 """ + if not passwd: + passwd = self.random_pass() + cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1') length = 64 try: - count = len(self.salt) + count = len(passwd) except TypeError: - # raise ServerError('Encrypt password error, TYpe error.') - pass + raise ServerError('Encrypt password error, TYpe error.') + add = (length - (count % length)) - self.salt += ('\0' * add) - cipher_text = cryptor.encrypt(self.salt) + passwd += ('\0' * add) + cipher_text = cryptor.encrypt(passwd) return b2a_hex(cipher_text) def decrypt(self, text): @@ -202,8 +196,6 @@ class PyCrypt(object): return plain_text.rstrip('\0') - - class ServerError(Exception): pass @@ -490,6 +482,22 @@ class Jasset(object): else: return False + def get__user(self): + perm_list = [] + asset_group_all = self.asset.bis_group.all() + for asset_group in asset_group_all: + perm_list.extend(asset_group.perm_set.all()) + + user_group_list = [] + for perm in perm_list: + user_group_list.append(perm.user_group) + + user_permed_list = [] + for user_group in user_group_list: + user_permed_list.extend(user_group.user_set.all()) + user_permed_list = list(set(user_permed_list)) + return user_permed_list + class JassetGroup(object): @@ -541,109 +549,22 @@ class JassetGroup(object): return len(self.get_asset()) -# def get_asset_group(user=None): -# """ -# Get user host_groups. -# 获取用户有权限的主机组 -# """ -# host_group_list = [] -# if user: -# user = user[0] +# def asset_perm_api(asset): +# if asset: # perm_list = [] -# user_group_all = user.group.all() -# for user_group in user_group_all: -# perm_list.extend(user_group.perm_set.all()) +# asset_group_all = asset.bis_group.all() +# for asset_group in asset_group_all: +# perm_list.extend(asset_group.perm_set.all()) # -# host_group_list = [] +# user_group_list = [] # for perm in perm_list: -# host_group_list.append(perm.asset_group) -# return host_group_list - - -# def get_asset_group_member(gid): -# """ -# Get host_group's member host -# 获取主机组下的主机 -# """ -# hosts = [] -# if gid: -# host_group = BisGroup.objects.filter(id=gid) -# if host_group: -# host_group = host_group[0] -# hosts = host_group.asset_set.all() -# return hosts - - -# def get_asset(user=None): -# """ -# Get the hosts of under the user control. -# 获取主机列表 -# """ -# hosts = [] -# if user: -# host_groups = get_asset_group(user) -# for host_group in host_groups: -# hosts.extend(get_asset_group_member(host_group.id)) -# return hosts - - -# def user_perm_asset_api(username): -# user = User.objects.filter(username=username) -# if user: -# user = user[0] -# asset_list = [] -# asset_group_list = user_perm_group_api(user) -# for asset_group in asset_group_list: -# asset_list.extend(asset_group.asset_set.all()) -# asset_list = list(set(asset_list)) -# return asset_list -# else: -# return [] - - -def asset_perm_api(asset): - if asset: - perm_list = [] - asset_group_all = asset.bis_group.all() - for asset_group in asset_group_all: - perm_list.extend(asset_group.perm_set.all()) - - user_group_list = [] - for perm in perm_list: - user_group_list.append(perm.user_group) - - user_permed_list = [] - for user_group in user_group_list: - user_permed_list.extend(user_group.user_set.all()) - user_permed_list = list(set(user_permed_list)) - return user_permed_list - - -def get_connect_item(user, ip): - asset = get_object(Asset, ip=ip) - port = int(asset.port) - - if not asset.is_active: - raise ServerError('Host %s is not active.' % ip) - - if not user.is_active: - raise ServerError('User %s is not active.' % user.username) - - login_type_dict = { - 'L': user.ldap_pwd, - } - - if asset.login_type in login_type_dict: - password = CRYPTOR.decrypt(login_type_dict[asset.login_type]) - return user.username, password, ip, port - - elif asset.login_type == 'M': - username = asset.username - password = CRYPTOR.decrypt(asset.password) - return username, password, ip, port - - else: - raise ServerError('Login type is not in ["L", "M"]') +# user_group_list.append(perm.user_group) +# +# user_permed_list = [] +# for user_group in user_group_list: +# user_permed_list.extend(user_group.user_set.all()) +# user_permed_list = list(set(user_permed_list)) +# return user_permed_list def validate(request, user_group=None, user=None, asset_group=None, asset=None, edept=None): @@ -747,31 +668,23 @@ def is_dir(dir_name, username='root', mode=0755): os.chmod(dir_name, mode) -def success(request, msg): +def http_success(request, msg): return render_to_response('success.html', locals()) -def httperror(request, emg): +def http_error(request, emg): message = emg return render_to_response('error.html', locals()) -def node_auth(request): - username = request.POST.get('username', ' ') - seed = request.POST.get('seed', ' ') - filename = request.POST.get('filename', ' ') - user = User.objects.filter(username=username, password=seed) - auth = 1 - if not user: - auth = 0 - if not filename.startswith('/opt/jumpserver/logs/connect/'): - auth = 0 - if auth: - result = {'auth': {'username': username, 'result': 'success'}} - else: - result = {'auth': {'username': username, 'result': 'failed'}} +CRYPTOR = PyCrypt(KEY) - return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json') +if LDAP_ENABLE: + LDAP_HOST_URL = CONF.get('ldap', 'host_url') + LDAP_BASE_DN = CONF.get('ldap', 'base_dn') + LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') + LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') + ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) +else: + ldap_conn = None - -CRYPTOR = PyCrypt(KEY) \ No newline at end of file diff --git a/jumpserver/views.py b/jumpserver/views.py index 31f5b9cfa..fd151d824 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -259,7 +259,7 @@ def install(request): User(id=5000, username="admin", password=md5_crypt('admin'), name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save() - return success(request, u'Jumpserver初始化成功') + return http_success(request, u'Jumpserver初始化成功') def download(request): @@ -327,3 +327,21 @@ def upload(request): return HttpResponse('传送成功') return render_to_response('upload.html', locals(), context_instance=RequestContext(request)) + + +def node_auth(request): + username = request.POST.get('username', ' ') + seed = request.POST.get('seed', ' ') + filename = request.POST.get('filename', ' ') + user = User.objects.filter(username=username, password=seed) + auth = 1 + if not user: + auth = 0 + if not filename.startswith('/opt/jumpserver/logs/connect/'): + auth = 0 + if auth: + result = {'auth': {'username': username, 'result': 'success'}} + else: + result = {'auth': {'username': username, 'result': 'failed'}} + + return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json')