mirror of https://github.com/jumpserver/jumpserver
commit
4b90cd9b82
29
connect.py
29
connect.py
|
@ -92,11 +92,20 @@ class Tty(object):
|
||||||
self.remote_ip = ''
|
self.remote_ip = ''
|
||||||
self.login_type = login_type
|
self.login_type = login_type
|
||||||
self.vim_flag = False
|
self.vim_flag = False
|
||||||
self.ps1_pattern = re.compile('\[.*@.*\][\$#]\s')
|
self.ps1_pattern = re.compile('\[?.*@.*\]?[\$#]\s')
|
||||||
self.vim_pattern = re.compile(r'\Wvi[m]+\s.* | \Wfg\s.*', re.X)
|
self.vim_pattern = re.compile(r'\W?vi[m]?\s.* | \W?fg\s.*', re.X)
|
||||||
self.vim_data = ''
|
self.vim_data = ''
|
||||||
self.stream = pyte.ByteStream()
|
self.stream = None
|
||||||
self.screen = None
|
self.screen = None
|
||||||
|
self.__init_screen_stream()
|
||||||
|
|
||||||
|
def __init_screen_stream(self):
|
||||||
|
"""
|
||||||
|
初始化虚拟屏幕和字符流
|
||||||
|
"""
|
||||||
|
self.stream = pyte.ByteStream()
|
||||||
|
self.screen = pyte.Screen(80, 24)
|
||||||
|
self.stream.attach(self.screen)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_output(strings):
|
def is_output(strings):
|
||||||
|
@ -125,12 +134,15 @@ class Tty(object):
|
||||||
result = match[-1].strip()
|
result = match[-1].strip()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def deal_command(self):
|
def deal_command(self, data):
|
||||||
"""
|
"""
|
||||||
处理截获的命令
|
处理截获的命令
|
||||||
|
:param data: 要处理的命令
|
||||||
:return:返回最后的处理结果
|
:return:返回最后的处理结果
|
||||||
"""
|
"""
|
||||||
command = ''
|
command = ''
|
||||||
|
try:
|
||||||
|
self.stream.feed(data)
|
||||||
# 从虚拟屏幕中获取处理后的数据
|
# 从虚拟屏幕中获取处理后的数据
|
||||||
for line in reversed(self.screen.buffer):
|
for line in reversed(self.screen.buffer):
|
||||||
line_data = "".join(map(operator.attrgetter("data"), line)).strip()
|
line_data = "".join(map(operator.attrgetter("data"), line)).strip()
|
||||||
|
@ -149,6 +161,8 @@ class Tty(object):
|
||||||
self.vim_flag = True
|
self.vim_flag = True
|
||||||
# 虚拟屏幕清空
|
# 虚拟屏幕清空
|
||||||
self.screen.reset()
|
self.screen.reset()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return command
|
return command
|
||||||
|
|
||||||
def get_log(self):
|
def get_log(self):
|
||||||
|
@ -348,16 +362,15 @@ class SshTty(Tty):
|
||||||
# 这个是用来处理用户的复制操作
|
# 这个是用来处理用户的复制操作
|
||||||
if input_str != x:
|
if input_str != x:
|
||||||
data += input_str
|
data += input_str
|
||||||
self.stream.feed(data)
|
|
||||||
if self.vim_flag:
|
if self.vim_flag:
|
||||||
match = self.ps1_pattern.search(self.vim_data)
|
match = self.ps1_pattern.search(self.vim_data)
|
||||||
if match:
|
if match:
|
||||||
self.vim_flag = False
|
self.vim_flag = False
|
||||||
data = self.deal_command()[0:200]
|
data = self.deal_command(data)[0:200]
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
||||||
else:
|
else:
|
||||||
data = self.deal_command()[0:200]
|
data = self.deal_command(data)[0:200]
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
|
||||||
data = ''
|
data = ''
|
||||||
|
@ -395,8 +408,6 @@ class SshTty(Tty):
|
||||||
win_size = self.get_win_size()
|
win_size = self.get_win_size()
|
||||||
# self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
|
# self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
|
||||||
self.channel = channel = transport.open_session()
|
self.channel = channel = transport.open_session()
|
||||||
self.screen = pyte.Screen(win_size[1], win_size[0])
|
|
||||||
self.stream.attach(self.screen)
|
|
||||||
channel.get_pty(term='xterm', height=win_size[0], width=win_size[1])
|
channel.get_pty(term='xterm', height=win_size[0], width=win_size[1])
|
||||||
channel.invoke_shell()
|
channel.invoke_shell()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -26,7 +26,7 @@ class PermSudo(models.Model):
|
||||||
class PermRole(models.Model):
|
class PermRole(models.Model):
|
||||||
name = models.CharField(max_length=100, unique=True)
|
name = models.CharField(max_length=100, unique=True)
|
||||||
comment = models.CharField(max_length=100, null=True, blank=True, default='')
|
comment = models.CharField(max_length=100, null=True, blank=True, default='')
|
||||||
password = models.CharField(max_length=100)
|
password = models.CharField(max_length=128)
|
||||||
key_path = models.CharField(max_length=100)
|
key_path = models.CharField(max_length=100)
|
||||||
date_added = models.DateTimeField(auto_now=True)
|
date_added = models.DateTimeField(auto_now=True)
|
||||||
sudo = models.ManyToManyField(PermSudo, related_name='perm_role')
|
sudo = models.ManyToManyField(PermSudo, related_name='perm_role')
|
||||||
|
|
|
@ -182,8 +182,9 @@ def gen_resource(ob, perm=None):
|
||||||
info = {'hostname': asset.hostname,
|
info = {'hostname': asset.hostname,
|
||||||
'ip': asset.ip,
|
'ip': asset.ip,
|
||||||
'port': asset_info.get('port', 22),
|
'port': asset_info.get('port', 22),
|
||||||
|
'ansible_ssh_private_key_file': role_key,
|
||||||
'username': role.name,
|
'username': role.name,
|
||||||
'password': CRYPTOR.decrypt(role.password)
|
# 'password': CRYPTOR.decrypt(role.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.path.isfile(role_key):
|
if os.path.isfile(role_key):
|
||||||
|
|
|
@ -519,12 +519,12 @@ def perm_role_push(request):
|
||||||
ret["pass_push"] = task.add_user(role.name)
|
ret["pass_push"] = task.add_user(role.name)
|
||||||
ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub'))
|
ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub'))
|
||||||
|
|
||||||
# 2. 推送账号密码
|
# 2. 推送账号密码 <为了安全 系统用户统一使用秘钥进行通信, 不再提供密码方式的推送>
|
||||||
elif password_push:
|
# elif password_push:
|
||||||
ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
|
# ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
|
||||||
|
|
||||||
# 3. 推送sudo配置文件
|
# 3. 推送sudo配置文件
|
||||||
if password_push or key_push:
|
if key_push:
|
||||||
sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
|
sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
|
||||||
if sudo_list:
|
if sudo_list:
|
||||||
ret['sudo'] = task.push_sudo_file([role], sudo_list)
|
ret['sudo'] = task.push_sudo_file([role], sudo_list)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[base]
|
[base]
|
||||||
url = http://192.168.244.129
|
url = http://192.168.10.165
|
||||||
key = i6k2zeu8x6mncl76
|
key = 941enj9neshd1wes
|
||||||
ip = 0.0.0.0
|
ip = 0.0.0.0
|
||||||
port = 80
|
port = 80
|
||||||
log = debug
|
log = debug
|
||||||
|
@ -14,9 +14,9 @@ database = jumpserver
|
||||||
|
|
||||||
[mail]
|
[mail]
|
||||||
mail_enable = 1
|
mail_enable = 1
|
||||||
email_host = smtp.exmail.qq.com
|
email_host =
|
||||||
email_port = 25
|
email_port = 587
|
||||||
email_host_user = noreply@jumpserver.org
|
email_host_user =
|
||||||
email_host_password = xxxxxxxxxx
|
email_host_password =
|
||||||
email_use_tls = True
|
email_use_tls = True
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import os.path
|
||||||
import threading
|
import threading
|
||||||
import re
|
import re
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from django.core.signals import request_started, request_finished
|
from django.core.signals import request_started, request_finished
|
||||||
|
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
|
@ -371,9 +370,10 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||||
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
|
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save()
|
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save()
|
||||||
|
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
|
||||||
|
if len(vim_data) > 0:
|
||||||
TtyLog(log=self.log, datetime=datetime.datetime.now(),
|
TtyLog(log=self.log, datetime=datetime.datetime.now(),
|
||||||
cmd=self.term.deal_command(self.term.data)[0:200]).save()
|
cmd=vim_data).save()
|
||||||
self.term.vim_data = ''
|
self.term.vim_data = ''
|
||||||
self.term.data = ''
|
self.term.data = ''
|
||||||
self.term.input_mode = False
|
self.term.input_mode = False
|
||||||
|
@ -412,7 +412,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
||||||
if self.term.vim_flag:
|
if self.term.vim_flag:
|
||||||
self.term.vim_data += recv
|
self.term.vim_data += recv
|
||||||
try:
|
try:
|
||||||
self.write_message(json.dumps({'data': data}))
|
self.write_message(data.decode('utf-8', 'replace'))
|
||||||
now_timestamp = time.time()
|
now_timestamp = time.time()
|
||||||
self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
|
self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
|
||||||
self.log_file_f.write(data)
|
self.log_file_f.write(data)
|
||||||
|
@ -460,7 +460,7 @@ def main():
|
||||||
}
|
}
|
||||||
tornado_app = tornado.web.Application(
|
tornado_app = tornado.web.Application(
|
||||||
[
|
[
|
||||||
(r'/monitor', MonitorHandler),
|
(r'/ws/monitor', MonitorHandler),
|
||||||
(r'/ws/terminal', WebTerminalHandler),
|
(r'/ws/terminal', WebTerminalHandler),
|
||||||
(r'/kill', WebTerminalKillHandler),
|
(r'/kill', WebTerminalKillHandler),
|
||||||
(r'/ws/exec', ExecHandler),
|
(r'/ws/exec', ExecHandler),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by liuzheng on 3/3/16.
|
* Created by liuzheng on 3/3/16.
|
||||||
*/
|
*/
|
||||||
|
@ -35,13 +36,12 @@ WSSHClient.prototype.connect = function (options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this._connection.onmessage = function (evt) {
|
this._connection.onmessage = function (evt) {
|
||||||
|
try{
|
||||||
|
options.onData(evt.data);
|
||||||
|
} catch (e) {
|
||||||
var data = JSON.parse(evt.data.toString());
|
var data = JSON.parse(evt.data.toString());
|
||||||
if (data.error !== undefined) {
|
|
||||||
options.onError(data.error);
|
options.onError(data.error);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
options.onData(data.data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this._connection.onclose = function (evt) {
|
this._connection.onclose = function (evt) {
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
var protocol = 'ws://';
|
var protocol = 'ws://';
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoint = protocol + document.URL.match(RegExp('//(.*?)/'))[1] + '/monitor';
|
var endpoint = protocol + document.URL.match(RegExp('//(.*?)/'))[1] + '/ws/monitor';
|
||||||
var file_path = obj.attr('file_path');
|
var file_path = obj.attr('file_path');
|
||||||
var socket = new WebSocket(endpoint + '?file_path=' + file_path);
|
var socket = new WebSocket(endpoint + '?file_path=' + file_path);
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -104,6 +104,10 @@ $('#roleForm').validator({
|
||||||
ok: "",
|
ok: "",
|
||||||
msg: {required: "系统用户名称必填"}
|
msg: {required: "系统用户名称必填"}
|
||||||
},
|
},
|
||||||
|
"role_password": {
|
||||||
|
rule: "length[0~64]",
|
||||||
|
tip: "系统密码"
|
||||||
|
},
|
||||||
"role_key": {
|
"role_key": {
|
||||||
rule: "check_begin",
|
rule: "check_begin",
|
||||||
ok: "",
|
ok: "",
|
||||||
|
|
|
@ -105,12 +105,16 @@ $('#roleForm').validator({
|
||||||
tip: "输入系统用户名称",
|
tip: "输入系统用户名称",
|
||||||
ok: "",
|
ok: "",
|
||||||
msg: {required: "系统用户名称必填"}
|
msg: {required: "系统用户名称必填"}
|
||||||
|
},
|
||||||
|
"role_password": {
|
||||||
|
rule: "length[0~64]",
|
||||||
|
tip: "系统密码"
|
||||||
},
|
},
|
||||||
"role_key": {
|
"role_key": {
|
||||||
rule: "check_begin",
|
rule: "check_begin",
|
||||||
ok: "",
|
ok: "",
|
||||||
empty: true
|
empty: true
|
||||||
},
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
valid: function(form) {
|
valid: function(form) {
|
||||||
|
|
|
@ -74,16 +74,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="j_group" class="col-sm-2 control-label">使用密码</label>
|
|
||||||
<div class="col-sm-1">
|
|
||||||
<div class="radio i-checks">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="1" id="use_password" name="use_password">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
Loading…
Reference in New Issue