mirror of https://github.com/jumpserver/jumpserver
Merge branch 'dev' of https://git.coding.net/jumpserver/jumpserver into dev
commit
0821e7cf41
|
@ -36,9 +36,10 @@ Web批量执行命令
|
|||
### 文档
|
||||
|
||||
* [访问wiki](https://github.com/ibuler/jumpserver/wiki)
|
||||
* [快速安装](https://github.com/ibuler/jumpserver/wiki/快速安装)
|
||||
* [名词解释](https://github.com/ibuler/jumpserver/wiki/名称解释)
|
||||
* [快速开始](https://github.com/ibuler/jumpserver/wiki/快速开始)
|
||||
* [快速安装](https://github.com/ibuler/jumpserver/wiki/Quickinstall)
|
||||
* [名词解释](https://github.com/ibuler/jumpserver/wiki/Termexplain)
|
||||
* [快速开始](https://github.com/ibuler/jumpserver/wiki/Quickstart)
|
||||
* [FAQ](https://github.com/ibuler/jumpserver/wiki/FAQs)
|
||||
|
||||
### 特点
|
||||
|
||||
|
|
13
connect.py
13
connect.py
|
@ -1,3 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
import sys
|
||||
|
@ -290,7 +291,7 @@ class Tty(object):
|
|||
|
||||
# 发起ssh连接请求 Make a ssh connection
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.load_system_host_keys()
|
||||
#ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
role_key = connect_info.get('role_key')
|
||||
|
@ -527,7 +528,7 @@ class Nav(object):
|
|||
user_asset_search = user_asset_all
|
||||
|
||||
self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search))
|
||||
color_print('[%-3s] %-12s %-15s %-5s %-10s %s' % ('ID', u'主机名', 'IP', u'端口', u'系统用户', u'备注'), 'title')
|
||||
color_print('[%-3s] %-12s %-15s %-5s %-10s %s' % ('ID', '主机名', 'IP', '端口', '系统用户', '备注'), 'title')
|
||||
for index, asset in self.search_result.items():
|
||||
# 获取该资产信息
|
||||
asset_info = get_asset_info(asset)
|
||||
|
@ -684,7 +685,7 @@ class Nav(object):
|
|||
res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm)
|
||||
runner = MyRunner(res)
|
||||
asset_name_str = ''
|
||||
print "匹配用户:\n"
|
||||
print "匹配主机:\n"
|
||||
for inv in runner.inventory.get_hosts(pattern=pattern):
|
||||
asset_name_str += '%s ' % inv.name
|
||||
print ' %s' % inv.name
|
||||
|
@ -734,7 +735,7 @@ def main():
|
|||
主程序
|
||||
"""
|
||||
if not login_user: # 判断用户是否存在
|
||||
color_print(u'没有该用户,或许你是以root运行的 No that user.', exits=True)
|
||||
color_print('没有该用户,或许你是以root运行的 No that user.', exits=True)
|
||||
|
||||
gid_pattern = re.compile(r'^g\d+$')
|
||||
nav = Nav(login_user)
|
||||
|
@ -799,7 +800,9 @@ def main():
|
|||
color_print('请输入正确ID', 'red')
|
||||
except ServerError, e:
|
||||
color_print(e, 'red')
|
||||
except IndexError:
|
||||
except Exception, e:
|
||||
color_print(e)
|
||||
time.sleep(5)
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -109,7 +109,7 @@ class PreSetup(object):
|
|||
|
||||
def _test_db_conn(self):
|
||||
try:
|
||||
MySQLdb.connect(host=self.db_host, port=self.db_port,
|
||||
MySQLdb.connect(host=self.db_host, port=int(self.db_port),
|
||||
user=self.db_user, passwd=self.db_pass, db=self.db)
|
||||
color_print('连接数据库成功', 'green')
|
||||
return True
|
||||
|
@ -127,8 +127,11 @@ class PreSetup(object):
|
|||
smtp.quit()
|
||||
return True
|
||||
|
||||
except (SMTPAuthenticationError, socket.timeout, socket.gaierror, SMTPSenderRefused, SMTPConnectError), e:
|
||||
except Exception, e:
|
||||
color_print(e, 'red')
|
||||
skip = raw_input('是否跳过(y/n) [n]? : ')
|
||||
if skip == 'y':
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
|
@ -147,7 +150,7 @@ class PreSetup(object):
|
|||
bash('pip install -r requirements.txt')
|
||||
|
||||
def _input_ip(self):
|
||||
ip = raw_input('\n请输入您服务器的IP地址,用户浏览器可以访问 [%s]: ' % get_ip_addr())
|
||||
ip = raw_input('\n请输入您服务器的IP地址,用户浏览器可以访问 [%s]: ' % get_ip_addr()).strip()
|
||||
self.ip = ip if ip else get_ip_addr()
|
||||
|
||||
def _input_mysql(self):
|
||||
|
@ -156,11 +159,11 @@ class PreSetup(object):
|
|||
if mysql != 'n':
|
||||
self._setup_mysql()
|
||||
else:
|
||||
db_host = raw_input('请输入数据库服务器IP [127.0.0.1]: ')
|
||||
db_port = int(raw_input('请输入数据库服务器端口 [3306]: '))
|
||||
db_user = raw_input('请输入数据库服务器用户 [root]: ')
|
||||
db_pass = raw_input('请输入数据库服务器密码: ')
|
||||
db = raw_input('请输入使用的数据库 [jumpserver]: ')
|
||||
db_host = raw_input('请输入数据库服务器IP [127.0.0.1]: ').strip()
|
||||
db_port = raw_input('请输入数据库服务器端口 [3306]: ').strip()
|
||||
db_user = raw_input('请输入数据库服务器用户 [root]: ').strip()
|
||||
db_pass = raw_input('请输入数据库服务器密码: ').strip()
|
||||
db = raw_input('请输入使用的数据库 [jumpserver]: ').strip()
|
||||
|
||||
if db_host: self.db_host = db_host
|
||||
if db_port: self.db_port = db_port
|
||||
|
|
|
@ -81,9 +81,9 @@ class Setup(object):
|
|||
os.system('id %s &> /dev/null || useradd %s' % (self.admin_user, self.admin_user))
|
||||
|
||||
@staticmethod
|
||||
def _cp_zzsh():
|
||||
os.chdir(os.path.join(jms_dir, 'install'))
|
||||
shutil.copy('zzjumpserver.sh', '/etc/profile.d/')
|
||||
def _ensure_sh():
|
||||
jshell = os.path.join(jms_dir, 'connect.py')
|
||||
os.chmod(jshell, 0755)
|
||||
|
||||
@staticmethod
|
||||
def _run_service():
|
||||
|
@ -97,7 +97,7 @@ class Setup(object):
|
|||
self._sync_db()
|
||||
self._input_admin()
|
||||
self._create_admin()
|
||||
self._cp_zzsh()
|
||||
self._ensure_sh()
|
||||
self._run_service()
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ "$USER" == "admin" ] || [ "$USER" == "root" ] || [ "$USER" == "" ];then
|
||||
echo ""
|
||||
else
|
||||
python /opt/jumpserver/connect.py
|
||||
if [ $USER == 'guanghongwei' ];then
|
||||
echo
|
||||
else
|
||||
exit 3
|
||||
echo
|
||||
fi
|
||||
fi
|
|
@ -545,7 +545,7 @@ def perm_role_push(request):
|
|||
if not failed_asset:
|
||||
msg = u'系统用户 %s 推送成功[ %s ]' % (role.name, ','.join(success_asset.keys()))
|
||||
else:
|
||||
error = u'系统用户 %s 推送失败 [ %s ], 推送成功 [ %s ]' % (role.name,
|
||||
error = u'系统用户 %s 推送失败 [ %s ], 推送成功 [ %s ] 进入系统用户详情,查看失败原因' % (role.name,
|
||||
','.join(failed_asset.keys()),
|
||||
','.join(success_asset.keys()))
|
||||
return my_render('jperm/perm_role_push.html', locals(), request)
|
||||
|
|
|
@ -352,6 +352,10 @@ def exec_cmd(request):
|
|||
def web_terminal(request):
|
||||
asset_id = request.GET.get('id')
|
||||
role_name = request.GET.get('role')
|
||||
asset = get_object(Asset, id=asset_id)
|
||||
if asset:
|
||||
print asset
|
||||
hostname = asset.hostname
|
||||
web_terminal_uri = 'ws://%s/terminal?id=%s&role=%s' % (WEB_SOCKET_HOST, asset_id, role_name)
|
||||
return render_to_response('jlog/web_terminal.html', locals())
|
||||
|
||||
|
|
|
@ -151,7 +151,8 @@ def server_add_user(username, password, ssh_key_pwd='', ssh_key_login_need=True)
|
|||
add a system user in jumpserver
|
||||
在jumpserver服务器上添加一个用户
|
||||
"""
|
||||
bash("useradd '%s'; echo '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, password, username))
|
||||
bash("useradd -s %s/connect.py '%s'; echo '%s'; echo '%s' | passwd --stdin '%s'" %
|
||||
(BASE_DIR, username, password, password, username))
|
||||
if ssh_key_login_need:
|
||||
gen_ssh_key(username, ssh_key_pwd)
|
||||
|
||||
|
|
|
@ -230,15 +230,14 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
|
|||
def on_message(self, message):
|
||||
data = json.loads(message)
|
||||
pattern = data.get('pattern', '')
|
||||
command = data.get('command', '')
|
||||
asset_name_str = ''
|
||||
if pattern and command:
|
||||
self.command = data.get('command', '')
|
||||
self.asset_name_str = ''
|
||||
if pattern and self.command:
|
||||
for inv in self.runner.inventory.get_hosts(pattern=pattern):
|
||||
asset_name_str += '%s ' % inv.name
|
||||
self.write_message('匹配主机: ' + asset_name_str)
|
||||
self.write_message('<span style="color: yellow">Ansible> %s</span>\n\n' % command)
|
||||
self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(command, pattern)))
|
||||
ExecLog(host=asset_name_str, cmd=command, user=self.user.username, remote_ip=self.remote_ip).save()
|
||||
self.asset_name_str += '%s ' % inv.name
|
||||
self.write_message('匹配主机: ' + self.asset_name_str)
|
||||
self.write_message('<span style="color: yellow">Ansible> %s</span>\n\n' % self.command)
|
||||
self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(self.command, pattern)))
|
||||
|
||||
for t in self.__class__.tasks:
|
||||
if t.is_alive():
|
||||
|
@ -251,11 +250,12 @@ class ExecHandler(tornado.websocket.WebSocketHandler):
|
|||
|
||||
def run_cmd(self, command, pattern):
|
||||
self.runner.run('shell', command, pattern=pattern)
|
||||
ExecLog(host=self.asset_name_str, cmd=self.command, user=self.user.username,
|
||||
remote_ip=self.remote_ip, result=self.runner.results).save()
|
||||
newline_pattern = re.compile(r'\n')
|
||||
for k, v in self.runner.results.items():
|
||||
for host, output in v.items():
|
||||
output = newline_pattern.sub('<br />', output)
|
||||
logger.debug(output)
|
||||
if k == 'ok':
|
||||
header = "<span style='color: green'>[ %s => %s]</span>\n" % (host, 'Ok')
|
||||
else:
|
||||
|
@ -333,7 +333,13 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||
data = json.loads(message)
|
||||
if not data:
|
||||
return
|
||||
if data.get('data'):
|
||||
|
||||
if 'resize' in data.get('data'):
|
||||
self.channel.resize_pty(
|
||||
data.get('data').get('resize').get('cols', 80),
|
||||
data.get('data').get('resize').get('rows', 24)
|
||||
)
|
||||
elif data.get('data'):
|
||||
self.term.input_mode = True
|
||||
if str(data['data']) in ['\r', '\n', '\r\n']:
|
||||
if self.term.vim_flag:
|
||||
|
@ -350,6 +356,8 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
|
|||
self.term.data = ''
|
||||
self.term.input_mode = False
|
||||
self.channel.send(data['data'])
|
||||
else:
|
||||
pass
|
||||
|
||||
def on_close(self):
|
||||
logger.debug('Websocket: Close request')
|
||||
|
|
|
@ -233,6 +233,7 @@
|
|||
layer.alert('没有授权系统用户')
|
||||
}
|
||||
else if (dataArray.length == 1 && data != 'error' && navigator.platform == 'Win32'){
|
||||
/*
|
||||
var title = 'Jumpserver Web Terminal' + '<span class="text-info"> '+ hostname +'</span>';
|
||||
layer.open({
|
||||
type: 2,
|
||||
|
@ -242,8 +243,10 @@
|
|||
area: ['628px', '420px'],
|
||||
content: new_url+data
|
||||
});
|
||||
*/
|
||||
window.open(new_url+data, '', 'width=628px, height=420px')
|
||||
} else if (dataArray.length == 1 && data != 'error'){
|
||||
layer.open({
|
||||
/*layer.open({
|
||||
type: 2,
|
||||
title: title,
|
||||
maxmin: true,
|
||||
|
@ -251,6 +254,9 @@
|
|||
area: ['628px', '452px'],
|
||||
content: new_url+data
|
||||
});
|
||||
*/
|
||||
window.open(new_url+data, '', 'width=628px, height=440px')
|
||||
|
||||
}
|
||||
else {
|
||||
aUrl = '';
|
||||
|
@ -276,6 +282,7 @@
|
|||
var hostname = $(a).attr('value');
|
||||
var title = 'Jumpserver Web Terminal - ' + '<span class="text-info"> '+ hostname +'</span>';
|
||||
if (navigator.platform == 'Win32'){
|
||||
/*
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: title,
|
||||
|
@ -284,8 +291,11 @@
|
|||
shade: false,
|
||||
content: new_url
|
||||
});
|
||||
*/
|
||||
window.open(new_url, '', 'height=628px, width=420px')
|
||||
|
||||
} else {
|
||||
/*
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: title,
|
||||
|
@ -294,6 +304,8 @@
|
|||
shade: false,
|
||||
content: new_url
|
||||
});
|
||||
*/
|
||||
window.open(new_url, '', 'height=628px, width=452px')
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -310,7 +322,6 @@
|
|||
shade: false,
|
||||
content: new_url
|
||||
});
|
||||
console.log(new_url);
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Jumpserver web terminal</title>
|
||||
<title>Jumpserver Web Terminal: {{ hostname }}</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
|
@ -37,6 +37,8 @@
|
|||
<script type="application/javascript" src="/static/js/term.js">
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
var rowHeight = 1;
|
||||
var colWidth = 1;
|
||||
function WSSHClient() {
|
||||
}
|
||||
|
||||
|
@ -101,7 +103,22 @@
|
|||
term.write(data);
|
||||
}
|
||||
}));
|
||||
rowHeight = 0.0 + 1.00 * $('.terminal').height() / 24 ;
|
||||
colWidth = 0.0 + 1.00 * $('.terminal').width() / 80;
|
||||
return {'term': term, 'client': client};
|
||||
}
|
||||
|
||||
function resize(){
|
||||
$('.terminal').css('width', window.innerWidth-25);
|
||||
console.log(window.innerWidth);
|
||||
console.log(window.innerWidth-10);
|
||||
var rows = Math.floor(window.innerHeight/rowHeight) - 1;
|
||||
var cols = Math.floor(window.innerWidth/colWidth) - 1;
|
||||
|
||||
return {rows: rows, cols: cols};
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script type='application/javascript'>
|
||||
|
@ -110,7 +127,17 @@
|
|||
};
|
||||
|
||||
$('#ssh').show();
|
||||
openTerminal(options);
|
||||
var term_client = openTerminal(options);
|
||||
console.log(rowHeight);
|
||||
|
||||
window.onresize = function(){
|
||||
var geom = resize();
|
||||
console.log(geom);
|
||||
term_client.term.resize(geom.cols, geom.rows);
|
||||
term_client.client.send({'resize': {'roles': geom.rows, 'cols': geom.cols}});
|
||||
$('#ssh').show();
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -204,7 +204,7 @@
|
|||
{% if info.success %}
|
||||
<td class="text-center" style="color: #1ab394;" >{{ info.success | yesno:"成功,失败,未知" }} </td>
|
||||
{% else %}
|
||||
<td class="text-center" style="color: #ec4758;cursor: help" title="{{ info.result }}" >{{ info.success | yesno:"成功,失败,未知" }} </td>
|
||||
<td class="text-center push_failed" style="color: #ec4758;cursor: help" title="{{ info.result }}">{{ info.success | yesno:"成功,失败,未知" }} </td>
|
||||
{% endif %}
|
||||
<td class="text-center" ><a class="fa fa-times del" href="{% url 'role_recycle' %}?role_id={{ role.id }}&asset_id={{ asset.id }}" style="color: #ec4758;"></a></td>
|
||||
</tr>
|
||||
|
@ -321,7 +321,16 @@
|
|||
});
|
||||
var url = '/jperm/role/push/?id={{ role.id }}&asset_id=' + check_array.join(',');
|
||||
$(this).attr('href', url)
|
||||
})
|
||||
});
|
||||
|
||||
$('.push_failed').click(function() {
|
||||
var fail_reason = $(this).attr('title');
|
||||
layer.alert(fail_reason, {
|
||||
skin: 'layui-layer-molv',
|
||||
area: '500px'
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<div class="col-sm-8">
|
||||
<select name="asset" id="asset" data-placeholder="请选择资产" class="chosen-select form-control m-b" multiple tabindex="2">
|
||||
{% for asset in assets %}
|
||||
<option value="{{ asset.id }}"{% if asset in rule.asset.all %} selected {% endif %}>{{ asset.ip }}</option>
|
||||
<option value="{{ asset.id }}"{% if asset in rule.asset.all %} selected {% endif %}>{{ asset.hostname }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="help-block m-b-none">资产和资产组必选一个</span>
|
||||
|
|
|
@ -113,12 +113,12 @@ $('#userForm').validator({
|
|||
|
||||
$("document").ready(function() {
|
||||
$("#regen_ssh_key").click(function () {
|
||||
alert('申请已提交,请等待,请勿重复提交');
|
||||
layer.alert('申请已提交,请等待,请勿重复提交');
|
||||
$.get(
|
||||
$(this).attr('value'),
|
||||
{},
|
||||
function(data){
|
||||
alert(data)
|
||||
layer.alert(data)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue