Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
2691cc0b
Commit
2691cc0b
authored
Feb 23, 2016
by
ibuler
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into dev
parents
4645029a
b1768565
Show whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
275 additions
and
87 deletions
+275
-87
.gitignore
.gitignore
+1
-0
README.md
README.md
+4
-0
connect.py
connect.py
+15
-6
install.py
install/install.py
+7
-0
next.py
install/next.py
+6
-5
zzjumpserver.sh
install/zzjumpserver.sh
+13
-0
asset_api.py
jasset/asset_api.py
+3
-4
models.py
jasset/models.py
+1
-1
views.py
jasset/views.py
+1
-1
log_api.py
jlog/log_api.py
+29
-0
views.py
jlog/views.py
+1
-1
ansible_api.py
jperm/ansible_api.py
+10
-0
views.py
jperm/views.py
+52
-13
api.py
jumpserver/api.py
+4
-2
settings.py
jumpserver/settings.py
+2
-1
views.py
jumpserver/views.py
+2
-2
user_api.py
juser/user_api.py
+2
-2
views.py
juser/views.py
+12
-13
run_websocket.py
run_websocket.py
+9
-9
exec_cmd.html
templates/exec_cmd.html
+7
-1
asset_cu_list.html
templates/jasset/asset_cu_list.html
+12
-0
asset_list.html
templates/jasset/asset_list.html
+5
-4
log_online.html
templates/jlog/log_online.html
+8
-2
web_terminal.html
templates/jlog/web_terminal.html
+7
-2
perm_role_add.html
templates/jperm/perm_role_add.html
+5
-12
perm_role_edit.html
templates/jperm/perm_role_edit.html
+11
-3
perm_role_list.html
templates/jperm/perm_role_list.html
+22
-1
perm_sudo_edit.html
templates/jperm/perm_sudo_edit.html
+21
-0
role_sudo.j2
templates/jperm/role_sudo.j2
+1
-1
user_add.html
templates/juser/user_add.html
+1
-1
upload.html
templates/upload.html
+1
-0
No files found.
.gitignore
View file @
2691cc0b
...
...
@@ -37,6 +37,7 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject
.settings
*.log
logs/*
keys/*
...
...
README.md
View file @
2691cc0b
## 写在前面
-
目前本版本处于beta阶段,请不要用于生产环境,除非你知道你在做什么
-
本版本暂时没加入LDAP接口,稳定版会将LDAP和无Agent方式抽象成API,2.x版本支持LDAP,请移步release中下载
#欢迎使用Jumpserver
**Jumpserver**
是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能。基于ssh协议来管理,客户端无需安装agent。
支持常见系统:
...
...
connect.py
View file @
2691cc0b
...
...
@@ -21,7 +21,7 @@ from io import open as copen
import
uuid
os
.
environ
[
'DJANGO_SETTINGS_MODULE'
]
=
'jumpserver.settings'
if
django
.
get_version
()
!=
'1.6'
:
if
not
django
.
get_version
()
.
startswith
(
'1.6'
)
:
setup
=
django
.
setup
()
from
django.contrib.sessions.models
import
Session
from
jumpserver.api
import
ServerError
,
User
,
Asset
,
PermRole
,
AssetGroup
,
get_object
,
mkdir
,
get_asset_info
...
...
@@ -33,7 +33,10 @@ from jperm.ansible_api import MyRunner
from
jlog.models
import
ExecLog
,
FileLog
login_user
=
get_object
(
User
,
username
=
getpass
.
getuser
())
remote_ip
=
os
.
popen
(
"who -m | awk '{ print $NF }'"
)
.
read
()
.
strip
(
'()
\n
'
)
try
:
remote_ip
=
os
.
environ
.
get
(
'SSH_CLIENT'
)
.
split
()[
0
]
except
(
IndexError
,
AttributeError
):
remote_ip
=
os
.
popen
(
"who -m | awk '{ print $NF }'"
)
.
read
()
.
strip
(
'()
\n
'
)
try
:
import
termios
...
...
@@ -412,7 +415,10 @@ class SshTty(Tty):
pass
if
sys
.
stdin
in
r
:
try
:
x
=
os
.
read
(
sys
.
stdin
.
fileno
(),
4096
)
except
OSError
:
pass
input_mode
=
True
if
str
(
x
)
in
[
'
\r
'
,
'
\n
'
,
'
\r\n
'
]:
if
self
.
vim_flag
:
...
...
@@ -576,12 +582,15 @@ class Nav(object):
role
=
role_check
[
int
(
role_id
)]
elif
len
(
roles
)
==
1
:
# 授权角色数为1
role
=
roles
[
0
]
else
:
color_print
(
'当前用户未被授予角色,无法执行任何操作,如有疑问请联系管理员。'
)
return
assets
=
list
(
self
.
user_perm
.
get
(
'role'
,
{})
.
get
(
role
)
.
get
(
'asset'
))
# 获取该用户,角色授权主机
print
"授权包含该系统用户的所有主机"
for
asset
in
assets
:
print
'
%
s'
%
asset
.
hostname
print
print
"请输入主机名或ansile支持的pattern, 多个主机:分隔, q退出"
print
"请输入主机名或ansi
b
le支持的pattern, 多个主机:分隔, q退出"
pattern
=
raw_input
(
"
\033
[1;32mPattern>:
\033
[0m "
)
.
strip
()
if
pattern
==
'q'
:
break
...
...
@@ -623,7 +632,7 @@ class Nav(object):
self
.
user_perm
=
get_group_user_perm
(
self
.
user
)
try
:
print
"进入批量上传模式"
print
"请输入主机名或ansile支持的pattern, 多个主机:分隔 q退出"
print
"请输入主机名或ansi
b
le支持的pattern, 多个主机:分隔 q退出"
pattern
=
raw_input
(
"
\033
[1;32mPattern>:
\033
[0m "
)
.
strip
()
if
pattern
==
'q'
:
break
...
...
@@ -676,7 +685,7 @@ class Nav(object):
self
.
user_perm
=
get_group_user_perm
(
self
.
user
)
try
:
print
"进入批量下载模式"
print
"请输入主机名或ansile支持的pattern, 多个主机:分隔,q退出"
print
"请输入主机名或ansi
b
le支持的pattern, 多个主机:分隔,q退出"
pattern
=
raw_input
(
"
\033
[1;32mPattern>:
\033
[0m "
)
.
strip
()
if
pattern
==
'q'
:
break
...
...
@@ -800,7 +809,7 @@ def main():
color_print
(
'请输入正确ID'
,
'red'
)
except
ServerError
,
e
:
color_print
(
e
,
'red'
)
except
Exception
,
e
:
except
IndexError
,
e
:
color_print
(
e
)
time
.
sleep
(
5
)
pass
...
...
install/install.py
View file @
2691cc0b
...
...
@@ -12,6 +12,8 @@ import socket
import
fcntl
import
struct
import
readline
import
random
import
string
jms_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
os
.
path
.
dirname
(
__file__
)))
sys
.
path
.
append
(
jms_dir
)
...
...
@@ -71,12 +73,15 @@ class PreSetup(object):
self
.
mail_addr
=
'hello@jumpserver.org'
self
.
mail_pass
=
''
self
.
ip
=
''
self
.
key
=
''
.
join
(
random
.
choice
(
string
.
ascii_lowercase
+
string
.
digits
)
\
for
_
in
range
(
16
))
def
write_conf
(
self
,
conf_file
=
os
.
path
.
join
(
jms_dir
,
'jumpserver.conf'
)):
color_print
(
'开始写入配置文件'
,
'green'
)
conf
=
ConfigParser
.
ConfigParser
()
conf
.
read
(
conf_file
)
conf
.
set
(
'base'
,
'url'
,
'http://
%
s'
%
self
.
ip
)
conf
.
set
(
'base'
,
'key'
,
self
.
key
)
conf
.
set
(
'db'
,
'host'
,
self
.
db_host
)
conf
.
set
(
'db'
,
'port'
,
self
.
db_port
)
conf
.
set
(
'db'
,
'user'
,
self
.
db_user
)
...
...
@@ -96,6 +101,7 @@ class PreSetup(object):
color_print
(
'默认用户名:
%
s 默认密码:
%
s'
%
(
self
.
db_user
,
self
.
db_pass
),
'green'
)
bash
(
'yum -y install mysql-server'
)
bash
(
'service mysqld start'
)
bash
(
'chkconfig mysqld on'
)
bash
(
'mysql -e "create database
%
s default charset=utf8"'
%
self
.
db
)
bash
(
'mysql -e "grant all on
%
s.* to
\'
%
s
\'
@
\'
%
s
\'
identified by
\'
%
s
\'
"'
%
(
self
.
db
,
self
.
db_user
,
...
...
@@ -105,6 +111,7 @@ class PreSetup(object):
@staticmethod
def
_set_env
():
color_print
(
'开始关闭防火墙和selinux'
,
'green'
)
os
.
system
(
"export LANG='en_US.UTF-8' && sed -i 's/LANG=.*/LANG=en_US.UTF-8/g' /etc/sysconfig/i18n"
)
bash
(
'service iptables stop && chkconfig iptables off && setenforce 0'
)
def
_test_db_conn
(
self
):
...
...
install/next.py
View file @
2691cc0b
...
...
@@ -18,7 +18,7 @@ if django.get_version() != '1.6':
from
juser.user_api
import
db_add_user
,
get_object
,
User
from
install
import
color_print
from
jumpserver.api
import
get_mac_address
from
jumpserver.api
import
get_mac_address
,
bash
socket
.
setdefaulttimeout
(
2
)
...
...
@@ -81,9 +81,10 @@ class Setup(object):
os
.
system
(
'id
%
s &> /dev/null || useradd
%
s'
%
(
self
.
admin_user
,
self
.
admin_user
))
@staticmethod
def
_ensure_sh
():
jshell
=
os
.
path
.
join
(
jms_dir
,
'connect.py'
)
os
.
chmod
(
jshell
,
0755
)
def
_cp_zzsh
():
os
.
chdir
(
os
.
path
.
join
(
jms_dir
,
'install'
))
shutil
.
copy
(
'zzjumpserver.sh'
,
'/etc/profile.d/'
)
bash
(
"sed -i 's#/opt/jumpserver#
%
s#g' /etc/profile.d/zzjumpserver.sh"
%
jms_dir
)
@staticmethod
def
_run_service
():
...
...
@@ -97,7 +98,7 @@ class Setup(object):
self
.
_sync_db
()
self
.
_input_admin
()
self
.
_create_admin
()
self
.
_
ensure_
sh
()
self
.
_
cp_zz
sh
()
self
.
_run_service
()
...
...
install/zzjumpserver.sh
0 → 100644
View file @
2691cc0b
#!/bin/bash
export
LANG
=
'zh_CN.UTF-8'
if
[
"
$USER
"
!=
"admin"
]
&&
[
"
$USER
"
!=
"root"
]
;
then
python /opt/jumpserver/connect.py
if
[
$USER
==
'guanghongwei'
]
;
then
echo
else
exit
3
echo
fi
fi
jasset/asset_api.py
View file @
2691cc0b
...
...
@@ -311,13 +311,12 @@ def excel_to_db(excel_file):
def
get_ansible_asset_info
(
asset_ip
,
setup_info
):
print
asset_ip
print
setup_info
,
'***'
disk_need
=
{}
disk_all
=
setup_info
.
get
(
"ansible_devices"
)
if
disk_all
:
for
disk_name
,
disk_info
in
disk_all
.
iteritems
():
print
disk_name
,
disk_info
if
disk_name
.
startswith
(
'sd'
)
or
disk_name
.
startswith
(
'hd'
)
or
disk_name
.
startswith
(
'vd'
):
if
disk_name
.
startswith
(
'sd'
)
or
disk_name
.
startswith
(
'hd'
)
or
disk_name
.
startswith
(
'vd'
)
or
disk_name
.
startswith
(
'xvd'
):
disk_size
=
disk_info
.
get
(
"size"
,
''
)
if
'M'
in
disk_size
:
disk_format
=
round
(
float
(
disk_size
[:
-
2
])
/
1000
,
0
)
...
...
@@ -334,7 +333,7 @@ def get_ansible_asset_info(asset_ip, setup_info):
mac
=
setup_info
.
get
(
"ansible_default_ipv4"
)
.
get
(
"macaddress"
)
brand
=
setup_info
.
get
(
"ansible_product_name"
)
cpu_type
=
setup_info
.
get
(
"ansible_processor"
)[
1
]
cpu_cores
=
setup_info
.
get
(
"ansible_processor_
count
"
)
cpu_cores
=
setup_info
.
get
(
"ansible_processor_
vcpus
"
)
cpu
=
cpu_type
+
' * '
+
unicode
(
cpu_cores
)
memory
=
setup_info
.
get
(
"ansible_memtotal_mb"
)
try
:
...
...
jasset/models.py
View file @
2691cc0b
...
...
@@ -75,7 +75,7 @@ class Asset(models.Model):
brand
=
models
.
CharField
(
max_length
=
64
,
blank
=
True
,
null
=
True
,
verbose_name
=
u'硬件厂商型号'
)
cpu
=
models
.
CharField
(
max_length
=
64
,
blank
=
True
,
null
=
True
,
verbose_name
=
u'CPU'
)
memory
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
null
=
True
,
verbose_name
=
u'内存'
)
disk
=
models
.
CharField
(
max_length
=
1
28
,
blank
=
True
,
null
=
True
,
verbose_name
=
u'硬盘'
)
disk
=
models
.
CharField
(
max_length
=
1
024
,
blank
=
True
,
null
=
True
,
verbose_name
=
u'硬盘'
)
system_type
=
models
.
CharField
(
max_length
=
32
,
blank
=
True
,
null
=
True
,
verbose_name
=
u"系统类型"
)
system_version
=
models
.
CharField
(
max_length
=
8
,
blank
=
True
,
null
=
True
,
verbose_name
=
u"系统版本号"
)
system_arch
=
models
.
CharField
(
max_length
=
16
,
blank
=
True
,
null
=
True
,
verbose_name
=
u"系统平台"
)
...
...
jasset/views.py
View file @
2691cc0b
...
...
@@ -227,7 +227,7 @@ def asset_edit(request):
if
use_default_auth
:
af_save
.
username
=
''
af_save
.
password
=
''
af_save
.
port
=
None
#
af_save.port = None
else
:
if
password
:
password_encode
=
CRYPTOR
.
encrypt
(
password
)
...
...
jlog/log_api.py
View file @
2691cc0b
...
...
@@ -6,7 +6,10 @@ from contextlib import closing
from
io
import
open
as
copen
from
json
import
dumps
from
math
import
ceil
import
datetime
import
time
import
re
import
os
from
os.path
import
basename
,
dirname
,
exists
,
join
from
struct
import
unpack
from
subprocess
import
Popen
...
...
@@ -17,6 +20,7 @@ from jinja2 import FileSystemLoader, Template
from
jinja2.environment
import
Environment
from
jumpserver.api
import
BASE_DIR
from
jlog.models
import
Log
DEFAULT_TEMPLATE
=
join
(
BASE_DIR
,
'templates'
,
'jlog'
,
'static.jinja2'
)
...
...
@@ -75,3 +79,28 @@ def renderTemplate(script_path, time_file_path, dimensions=(24, 80), templatenam
return
rendered
def
kill_invalid_connection
():
long_time_logs
=
[]
unfinished_logs
=
Log
.
objects
.
filter
(
is_finished
=
False
)
now
=
datetime
.
datetime
.
now
()
now_timestamp
=
int
(
time
.
mktime
(
now
.
timetuple
()))
for
log
in
unfinished_logs
:
if
(
now
-
log
.
start_time
)
.
days
>
1
:
long_time_logs
.
append
(
log
)
for
log
in
long_time_logs
:
try
:
log_file_mtime
=
int
(
os
.
stat
(
log
.
log_path
)
.
st_mtime
)
except
OSError
:
log_file_mtime
=
0
if
(
now_timestamp
-
log_file_mtime
)
>
3600
:
try
:
os
.
kill
(
int
(
log
.
pid
),
9
)
except
OSError
:
pass
log
.
is_finished
=
True
log
.
end_time
=
now
log
.
save
()
jlog/views.py
View file @
2691cc0b
...
...
@@ -66,7 +66,7 @@ def log_list(request, offset):
contact_list
,
p
,
contacts
,
page_range
,
current_page
,
show_first
,
show_end
=
pages
(
posts
,
request
)
web_monitor_uri
=
'
ws://
%
s/monitor'
%
WEB_SOCKET_HOST
web_monitor_uri
=
'
%
s/monitor'
%
WEB_SOCKET_HOST
web_kill_uri
=
'http://
%
s/kill'
%
WEB_SOCKET_HOST
session_id
=
request
.
session
.
session_key
return
render_to_response
(
'jlog/log_
%
s.html'
%
offset
,
locals
(),
context_instance
=
RequestContext
(
request
))
...
...
jperm/ansible_api.py
View file @
2691cc0b
...
...
@@ -365,6 +365,16 @@ class MyTask(MyRunner):
self
.
run
(
"user"
,
module_args
,
become
=
True
)
return
self
.
results
def
del_user_sudo
(
self
,
username
):
"""
delete a role sudo item
:param username:
:return:
"""
module_args
=
"sed -i 's/^
%
s.*//' /etc/sudoers"
%
username
self
.
run
(
"command"
,
module_args
,
become
=
True
)
return
self
.
results
@staticmethod
def
gen_sudo_script
(
role_list
,
sudo_list
):
# receive role_list = [role1, role2] sudo_list = [sudo1, sudo2]
...
...
jperm/views.py
View file @
2691cc0b
...
...
@@ -16,8 +16,8 @@ from jperm.perm_api import get_role_info, get_role_push_host
from
jumpserver.api
import
my_render
,
get_object
,
CRYPTOR
# 设置PERM APP Log
from
jumpserver.
settings
import
LOG_LEVEL
logger
=
set_log
(
LOG_LEVEL
,
filename
=
'jumpserver_perm.log'
)
from
jumpserver.
api
import
logger
#
logger = set_log(LOG_LEVEL, filename='jumpserver_perm.log')
@require_role
(
'admin'
)
...
...
@@ -277,7 +277,7 @@ def perm_role_add(request):
if
request
.
method
==
"POST"
:
# 获取参数: name, comment
name
=
request
.
POST
.
get
(
"role_name"
,
""
)
name
=
request
.
POST
.
get
(
"role_name"
,
""
)
.
strip
()
comment
=
request
.
POST
.
get
(
"role_comment"
,
""
)
password
=
request
.
POST
.
get
(
"role_password"
,
""
)
key_content
=
request
.
POST
.
get
(
"role_key"
,
""
)
...
...
@@ -286,6 +286,8 @@ def perm_role_add(request):
try
:
if
get_object
(
PermRole
,
name
=
name
):
raise
ServerError
(
u'已经存在该用户
%
s'
%
name
)
if
name
==
"root"
:
raise
ServerError
(
u'禁止使用root用户作为系统用户,这样非常危险!'
)
default
=
get_object
(
Setting
,
name
=
'default'
)
if
password
:
...
...
@@ -317,14 +319,37 @@ def perm_role_delete(request):
"""
delete role page
"""
if
request
.
method
==
"GET"
:
try
:
# 获取参数删除的role对象
role_id
=
request
.
GET
.
get
(
"id"
)
role
=
get_object
(
PermRole
,
id
=
role_id
)
if
not
role
:
logger
.
warning
(
u"Delete Role: role_id
%
s not exist"
%
role_id
)
raise
ServerError
(
u"role_id
%
s 无数据记录"
%
role_id
)
# 删除推送到主机上的role
filter_type
=
request
.
GET
.
get
(
"filter_type"
)
print
filter_type
if
filter_type
:
if
filter_type
==
"recycle_assets"
:
recycle_assets
=
[
push
.
asset
for
push
in
role
.
perm_push
.
all
()
if
push
.
success
]
print
recycle_assets
recycle_assets_ip
=
','
.
join
([
asset
.
ip
for
asset
in
recycle_assets
])
return
HttpResponse
(
recycle_assets_ip
)
else
:
return
HttpResponse
(
"no such filter_type:
%
s"
%
filter_type
)
else
:
return
HttpResponse
(
"filter_type: ?"
)
except
ServerError
,
e
:
return
HttpResponse
(
e
)
if
request
.
method
==
"POST"
:
try
:
# 获取参数删除的role对象
role_id
=
request
.
POST
.
get
(
"id"
)
role
=
get_object
(
PermRole
,
id
=
role_id
)
if
not
role
:
logger
.
warning
(
u"Delete Role:
%
s not exist"
%
role
.
name
)
raise
ServerError
(
u"
%
s 无数据记录"
%
role
.
name
)
logger
.
warning
(
u"Delete Role:
role_id
%
s not exist"
%
role_id
)
raise
ServerError
(
u"
role_id
%
s 无数据记录"
%
role_id
)
role_key
=
role
.
key_path
# 删除推送到主机上的role
recycle_assets
=
[
push
.
asset
for
push
in
role
.
perm_push
.
all
()
if
push
.
success
]
...
...
@@ -333,11 +358,13 @@ def perm_role_delete(request):
recycle_resource
=
gen_resource
(
recycle_assets
)
task
=
MyTask
(
recycle_resource
)
try
:
msg
=
task
.
del_user
(
get_object
(
PermRole
,
id
=
role_id
)
.
name
)
msg_del_user
=
task
.
del_user
(
get_object
(
PermRole
,
id
=
role_id
)
.
name
)
msg_del_sudo
=
task
.
del_user_sudo
(
get_object
(
PermRole
,
id
=
role_id
)
.
name
)
except
Exception
,
e
:
logger
.
warning
(
u"Recycle Role failed:
%
s"
%
e
)
raise
ServerError
(
u"回收已推送的系统用户失败:
%
s"
%
e
)
logger
.
info
(
u"delete role
%
s - execute delete user:
%
s"
%
(
role
.
name
,
msg
))
logger
.
info
(
u"delete role
%
s - execute delete user:
%
s"
%
(
role
.
name
,
msg_del_user
))
logger
.
info
(
u"delete role
%
s - execute delete sudo:
%
s"
%
(
role
.
name
,
msg_del_sudo
))
# TODO: 判断返回结果,处理异常
# 删除存储的秘钥,以及目录
try
:
...
...
@@ -423,6 +450,9 @@ def perm_role_edit(request):
if
not
role
:
raise
ServerError
(
'该系统用户不能存在'
)
if
role_name
==
"root"
:
raise
ServerError
(
u'禁止使用root用户作为系统用户,这样非常危险!'
)
if
role_password
:
encrypt_pass
=
CRYPTOR
.
encrypt
(
role_password
)
role
.
password
=
encrypt_pass
...
...
@@ -473,6 +503,7 @@ def perm_role_push(request):
for
asset_group
in
asset_groups_obj
:
group_assets_obj
.
extend
(
asset_group
.
asset_set
.
all
())
calc_assets
=
list
(
set
(
assets_obj
)
|
set
(
group_assets_obj
))
push_resource
=
gen_resource
(
calc_assets
)
# 调用Ansible API 进行推送
...
...
@@ -577,14 +608,17 @@ def perm_sudo_add(request):
"""
# 渲染数据
header_title
,
path1
,
path2
=
"Sudo命令"
,
"别名管理"
,
"添加别名"
try
:
if
request
.
method
==
"POST"
:
# 获取参数: name, comment
name
=
request
.
POST
.
get
(
"sudo_name"
)
.
strip
()
.
upper
()
comment
=
request
.
POST
.
get
(
"sudo_comment"
)
.
strip
()
commands
=
request
.
POST
.
get
(
"sudo_commands"
)
.
strip
()
pattern
=
re
.
compile
(
r'[ \n,\r]'
)
if
not
name
or
not
commands
:
raise
ServerError
(
u"sudo name 和 commands是必填项!"
)
pattern
=
re
.
compile
(
r'[\n,\r]'
)
commands
=
', '
.
join
(
list_drop_str
(
pattern
.
split
(
commands
),
u''
))
logger
.
debug
(
u'添加sudo
%
s:
%
s'
%
(
name
,
commands
))
...
...
@@ -594,8 +628,8 @@ def perm_sudo_add(request):
sudo
=
PermSudo
(
name
=
name
.
strip
(),
comment
=
comment
,
commands
=
commands
)
sudo
.
save
()
msg
=
u"添加Sudo命令别名:
%
s"
%
name
# 渲染数据
except
ServerError
,
e
:
error
=
e
return
my_render
(
'jperm/perm_sudo_add.html'
,
locals
(),
request
)
...
...
@@ -612,12 +646,16 @@ def perm_sudo_edit(request):
sudo_id
=
request
.
GET
.
get
(
"id"
)
sudo
=
PermSudo
.
objects
.
get
(
id
=
sudo_id
)
try
:
if
request
.
method
==
"POST"
:
name
=
request
.
POST
.
get
(
"sudo_name"
)
.
upper
()
commands
=
request
.
POST
.
get
(
"sudo_commands"
)
comment
=
request
.
POST
.
get
(
"sudo_comment"
)
pattern
=
re
.
compile
(
r'[ \n,\r]'
)
if
not
name
or
not
commands
:
raise
ServerError
(
u"sudo name 和 commands是必填项!"
)
pattern
=
re
.
compile
(
r'[\n,\r]'
)
commands
=
', '
.
join
(
list_drop_str
(
pattern
.
split
(
commands
),
u''
))
.
strip
()
logger
.
debug
(
u'添加sudo
%
s:
%
s'
%
(
name
,
commands
))
...
...
@@ -627,7 +665,8 @@ def perm_sudo_edit(request):
sudo
.
save
()
msg
=
u"更新命令别名:
%
s"
%
name
except
ServerError
,
e
:
error
=
e
return
my_render
(
'jperm/perm_sudo_edit.html'
,
locals
(),
request
)
...
...
jumpserver/api.py
View file @
2691cc0b
...
...
@@ -64,7 +64,6 @@ def get_asset_info(asset):
info
=
{
'hostname'
:
asset
.
hostname
,
'ip'
:
asset
.
ip
}
if
asset
.
use_default_auth
:
if
default
:
info
[
'port'
]
=
int
(
default
.
field2
)
info
[
'username'
]
=
default
.
field1
try
:
info
[
'password'
]
=
CRYPTOR
.
decrypt
(
default
.
field3
)
...
...
@@ -73,9 +72,12 @@ def get_asset_info(asset):
if
os
.
path
.
isfile
(
default
.
field4
):
info
[
'ssh_key'
]
=
default
.
field4
else
:
info
[
'port'
]
=
int
(
asset
.
port
)
info
[
'username'
]
=
asset
.
username
info
[
'password'
]
=
CRYPTOR
.
decrypt
(
asset
.
password
)
try
:
info
[
'port'
]
=
int
(
asset
.
port
)
except
TypeError
:
info
[
'port'
]
=
int
(
default
.
field2
)
return
info
...
...
jumpserver/settings.py
View file @
2691cc0b
...
...
@@ -152,5 +152,6 @@ STATIC_URL = '/static/'
BOOTSTRAP_COLUMN_COUNT
=
10
CRONJOBS
=
[
(
'0 1 * * *'
,
'jasset.asset_api.asset_ansible_update_all'
)
(
'0 1 * * *'
,
'jasset.asset_api.asset_ansible_update_all'
),
(
'1 * * * *'
,
'jlog.log_api.kill_invalid_connection'
),
]
jumpserver/views.py
View file @
2691cc0b
...
...
@@ -344,7 +344,7 @@ def download(request):
def
exec_cmd
(
request
):
role
=
request
.
GET
.
get
(
'role'
)
check_assets
=
request
.
GET
.
get
(
'check_assets'
,
''
)
web_terminal_uri
=
'
ws://
%
s/exec?role=
%
s'
%
(
WEB_SOCKET_HOST
,
role
)
web_terminal_uri
=
'
%
s/exec?role=
%
s'
%
(
WEB_SOCKET_HOST
,
role
)
return
my_render
(
'exec_cmd.html'
,
locals
(),
request
)
...
...
@@ -356,7 +356,7 @@ def web_terminal(request):
if
asset
:
print
asset
hostname
=
asset
.
hostname
web_terminal_uri
=
'
ws://
%
s/terminal?id=
%
s&role=
%
s'
%
(
WEB_SOCKET_HOST
,
asset_id
,
role_name
)
web_terminal_uri
=
'
%
s/terminal?id=
%
s&role=
%
s'
%
(
WEB_SOCKET_HOST
,
asset_id
,
role_name
)
return
render_to_response
(
'jlog/web_terminal.html'
,
locals
())
juser/user_api.py
View file @
2691cc0b
...
...
@@ -151,8 +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
%
s/connect.py '
%
s'; echo '
%
s'; echo '
%
s' | passwd --stdin '
%
s'"
%
(
BASE_DIR
,
username
,
password
,
password
,
username
))
bash
(
"useradd
'
%
s'; echo '
%
s'; echo '
%
s:
%
s' | chpasswd "
%
(
username
,
password
,
username
,
password
))
if
ssh_key_login_need
:
gen_ssh_key
(
username
,
ssh_key_pwd
)
...
...
juser/views.py
View file @
2691cc0b
...
...
@@ -6,7 +6,7 @@
# from Crypto.PublicKey import RSA
import
uuid
from
django.contrib.auth.decorators
import
login_required
from
django.shortcuts
import
get_object_or_404
from
django.db.models
import
Q
from
juser.user_api
import
*
from
jperm.perm_api
import
get_group_user_perm
...
...
@@ -111,22 +111,18 @@ def group_edit(request):
if
len
(
UserGroup
.
objects
.
filter
(
name
=
group_name
))
>
1
:
raise
ServerError
(
u'
%
s 用户组已存在'
%
group_name
)
# add user group
user_group
=
get_object_or_404
(
UserGroup
,
id
=
group_id
)
user_group
.
user_set
.
clear
()
for
user
in
User
.
objects
.
filter
(
id__in
=
users_selected
):
user
.
group
.
add
(
UserGroup
.
objects
.
get
(
id
=
group_id
))
# delete user group
user_group
=
UserGroup
.
objects
.
get
(
id
=
group_id
)
for
user
in
[
user
for
user
in
User
.
objects
.
filter
(
group
=
user_group
)
if
user
not
in
User
.
objects
.
filter
(
id__in
=
users_selected
)]:
user_group_all
=
user
.
group
.
all
()
user
.
group
.
clear
()
for
g
in
user_group_all
:
if
g
==
user_group
:
continue
user
.
group
.
add
(
g
)
user_group
.
name
=
group_name
user_group
.
comment
=
comment
user_group
.
save
()
except
ServerError
,
e
:
error
=
e
if
not
error
:
return
HttpResponseRedirect
(
reverse
(
'user_group_list'
))
else
:
...
...
@@ -313,6 +309,12 @@ def reset_password(request):
hash_encode
=
request
.
GET
.
get
(
'hash'
,
''
)
action
=
'/juser/password/reset/?uuid=
%
s×tamp=
%
s&hash=
%
s'
%
(
uuid_r
,
timestamp
,
hash_encode
)
if
hash_encode
==
PyCrypt
.
md5_crypt
(
uuid_r
+
timestamp
+
KEY
):
if
int
(
time
.
time
())
-
int
(
timestamp
)
>
600
:
return
http_error
(
request
,
u'链接已超时'
)
else
:
return
HttpResponse
(
'hash校验失败'
)
if
request
.
method
==
'POST'
:
password
=
request
.
POST
.
get
(
'password'
)
password_confirm
=
request
.
POST
.
get
(
'password_confirm'
)
...
...
@@ -328,9 +330,6 @@ def reset_password(request):
else
:
return
HttpResponse
(
'用户不存在'
)
if
hash_encode
==
PyCrypt
.
md5_crypt
(
uuid_r
+
timestamp
+
KEY
):
if
int
(
time
.
time
())
-
int
(
timestamp
)
>
600
:
return
http_error
(
request
,
u'链接已超时'
)
else
:
return
render_to_response
(
'juser/reset_password.html'
,
locals
())
...
...
run_websocket.py
View file @
2691cc0b
...
...
@@ -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
:
...
...
templates/exec_cmd.html
View file @
2691cc0b
...
...
@@ -23,7 +23,13 @@
<script
type=
"text/javascript"
>
var
wsUri
=
"{{ web_terminal_uri }}"
;
//请求的websocket url
var
protocol
=
"ws://"
;
if
(
window
.
location
.
protocol
==
'https:'
)
{
protocol
=
'wss://'
;
}
var
wsUri
=
protocol
+
"{{ web_terminal_uri }}"
;
//请求的websocket url
var
ws
=
new
WebSocket
(
wsUri
);
function
createSystemMessage
(
message
)
{
...
...
templates/jasset/asset_cu_list.html
View file @
2691cc0b
...
...
@@ -224,6 +224,7 @@
}
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,
title: title,
...
...
@@ -232,7 +233,10 @@
area: ['628px', '420px'],
content: new_url+data
});
*/
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=380px'
);
}
else
if
(
dataArray
.
length
==
1
&&
data
!=
'error'
){
/*
layer.open({
type: 2,
title: title,
...
...
@@ -241,6 +245,8 @@
area: ['628px', '452px'],
content: new_url+data
});
*/
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=452px'
)
}
else
{
aUrl
=
''
;
...
...
@@ -266,6 +272,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,
...
...
@@ -274,8 +281,11 @@
shade: false,
content: new_url
});
*/
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=380px'
);
}
else
{
/*
layer.open({
type: 2,
title: title,
...
...
@@ -284,6 +294,8 @@
shade: false,
content: new_url
});
*/
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=452px'
);
}
return
false
...
...
templates/jasset/asset_list.html
View file @
2691cc0b
...
...
@@ -243,8 +243,9 @@
area: ['628px', '420px'],
content: new_url+data
});
window.open(new_url+data, '_blank', 'toolbar=yes, location=yes, scrollbars=yes, resizable=yes, copyhistory=yes, width=628, height=400')
*/
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=
420px'
)
window
.
open
(
new_url
+
data
,
''
,
'width=628px, height=
380px'
);
}
else
if
(
dataArray
.
length
==
1
&&
data
!=
'error'
){
/*layer.open({
type: 2,
...
...
@@ -255,7 +256,7 @@
content: new_url+data
});
*/
window
.
open
(
new_url
+
data
,
'
'
,
'width=628px, height=440px'
)
window
.
open
(
new_url
+
data
,
'
_blank'
,
'toolbar=yes, location=yes, copyhistory=yes, scrollbars=yes, width=628, height=410'
);
}
else
{
...
...
@@ -292,7 +293,7 @@
content: new_url
});
*/
window
.
open
(
new_url
,
'
'
,
'height=628px, width=420px
'
)
window
.
open
(
new_url
,
'
_blank'
,
'toolbar=yes, location=yes, copyhistory=yes, scrollbars=yes, width=628, height=400
'
)
}
else
{
/*
...
...
@@ -305,7 +306,7 @@
content: new_url
});
*/
window
.
open
(
new_url
,
'
'
,
'height=628px, width=452px'
)
window
.
open
(
new_url
,
'
_blank'
,
'toolbar=yes, location=yes, copyhistory=yes, scrollbars=yes, width=628, height=410'
);
}
return
false
...
...
templates/jlog/log_online.html
View file @
2691cc0b
...
...
@@ -136,14 +136,20 @@
{
#
})
#
}
{
#
});
#
}
function
init
(
obj
){
var
protocol
=
"ws://"
;
if
(
window
.
location
.
protocol
==
'https:'
)
{
protocol
=
'wss://'
;
}
var
file_path
=
obj
.
attr
(
'file_path'
);
var
wsUri
=
'{{ web_monitor_uri }}'
;
var
wsUri
=
protocol
+
'{{ web_monitor_uri }}'
;
var
socket
=
new
WebSocket
(
wsUri
+
'?file_path='
+
file_path
);
var
term
=
new
Terminal
({
cols
:
80
,
rows
:
24
,
screenKeys
:
false
screenKeys
:
false
,
handler
:
function
(){
return
false
}
});
var
tag
=
$
(
'<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>'
);
...
...
templates/jlog/web_terminal.html
View file @
2691cc0b
...
...
@@ -43,7 +43,12 @@
}
WSSHClient
.
prototype
.
connect
=
function
(
options
)
{
var
endpoint
=
'{{ web_terminal_uri }}'
;
var
protocol
=
"ws://"
;
if
(
window
.
location
.
protocol
==
'https:'
)
{
protocol
=
'wss://'
;
}
var
endpoint
=
protocol
+
'{{ web_terminal_uri }}'
;
if
(
window
.
WebSocket
)
{
this
.
_connection
=
new
WebSocket
(
endpoint
);
...
...
@@ -112,7 +117,7 @@
$
(
'.terminal'
).
css
(
'width'
,
window
.
innerWidth
-
25
);
console
.
log
(
window
.
innerWidth
);
console
.
log
(
window
.
innerWidth
-
10
);
var
rows
=
Math
.
floor
(
window
.
innerHeight
/
rowHeight
)
-
1
;
var
rows
=
Math
.
floor
(
window
.
innerHeight
/
rowHeight
)
-
2
;
var
cols
=
Math
.
floor
(
window
.
innerWidth
/
colWidth
)
-
1
;
return
{
rows
:
rows
,
cols
:
cols
};
...
...
templates/jperm/perm_role_add.html
View file @
2691cc0b
...
...
@@ -92,11 +92,9 @@ $('#roleForm').validator({
timely
:
2
,
theme
:
"yellow_right_effect"
,
rules
:
{
check_name
:
[
/^
\w{2,20}
$/
,
'大小写字母数字和下划线,2-20位'
],
check_begin
:
[
/^
[\-]
+BEGIN RSA PRIVATE KEY
[\-]
+/gm
,
'RSA Key填写有误,请检查'
],
{
#
either
:
function
(){
#
}
{
#
return
$
(
'#role_password'
).
val
()
==
''
#
}
{
#
}
#
}
check_name
:
[
/
(?!
^root$
)
^
[\w
.
]{2,20}
$/i
,
'大小写字母数字和下划线小数点,2-20位,并且非root'
],
check_begin
:
[
/^
[\-]
+BEGIN R|DSA PRIVATE KEY
[\-]
+/gm
,
'RSA|DSA Key填写有误,请检查'
]
},
fields
:
{
...
...
@@ -110,13 +108,8 @@ $('#roleForm').validator({
rule
:
"check_begin"
,
ok
:
""
,
empty
:
true
},
{
#
"role_key"
:
{
#
}
{
#
rule
:
"required(either)"
,
#
}
{
#
tip
:
"输入密钥"
,
#
}
{
#
ok
:
""
,
#
}
{
#
msg
:
{
required
:
"密码和密钥必填一个!"
}
#
}
{
#
}
#
}
}
},
valid
:
function
(
form
)
{
form
.
submit
();
...
...
templates/jperm/perm_role_edit.html
View file @
2691cc0b
...
...
@@ -94,16 +94,24 @@ $('#roleForm').validator({
timely
:
2
,
theme
:
"yellow_right_effect"
,
rules
:
{
check_name
:
[
/^
\w{2,20}
$/
,
'大小写字母数字和下划线,2-20位'
]
check_name
:
[
/
(?!
^root$
)
^
\w{2,20}
$/i
,
'大小写字母数字和下划线,2-20位,并且非root'
],
check_begin
:
[
/^
[\-]
+BEGIN RSA PRIVATE KEY
[\-]
+/gm
,
'RSA Key填写有误,请检查'
],
},
fields
:
{
"role_name"
:
{
rule
:
"required;check_name"
,
rule
:
"required;check_name
;check_name_root
"
,
tip
:
"输入系统用户名称"
,
ok
:
""
,
msg
:
{
required
:
"系统用户名称必填"
}
}
},
"role_key"
:
{
rule
:
"check_begin"
,
ok
:
""
,
empty
:
true
},
},
valid
:
function
(
form
)
{
form
.
submit
();
...
...
templates/jperm/perm_role_list.html
View file @
2691cc0b
...
...
@@ -84,7 +84,19 @@
<script>
function
remove_role
(
role_id
){
if
(
confirm
(
"确认删除"
))
{
$
.
ajax
({
type
:
"GET"
,
url
:
"{% url 'role_del' %}"
,
data
:
{
id
:
role_id
,
filter_type
:
'recycle_assets'
},
success
:
function
(
data
)
{
console
.
log
(
data
)
if
(
data
)
{
msg
=
data
+
"的系统用户会被删除,包括其家目录,请谨慎操作!"
}
else
{
msg
=
"该角色无已推送的系统用户, 可以安全删除"
}
if
(
confirm
(
msg
))
{
$
.
ajax
({
type
:
"POST"
,
url
:
"{% url 'role_del' %}"
,
...
...
@@ -100,6 +112,15 @@ function remove_role(role_id){
}
});
}
},
error
:
function
(
error
)
{
alert
(
error
)
},
});
}
</script>
...
...
templates/jperm/perm_sudo_edit.html
View file @
2691cc0b
...
...
@@ -112,7 +112,28 @@ var config = {
for
(
var
selector
in
config
)
{
$
(
selector
).
chosen
(
config
[
selector
]);
}
$
(
'#sudoForm'
).
validator
({
timely
:
2
,
theme
:
"yellow_right_effect"
,
rules
:
{
check_name
:
[
/^
\w{2,20}
$/
,
'大写字母,2-20位'
]
},
fields
:
{
"sudo_name"
:
{
rule
:
"required;check_name"
},
"sudo_runas"
:
{
rule
:
"required;check_name"
},
"sudo_commands"
:
{
rule
:
"required"
}
},
valid
:
function
(
form
)
{
form
.
submit
();
}
});
</script>
<script
src=
"/static/js/cropper/cropper.min.js"
></script>
<script
src=
"/static/js/datapicker/bootstrap-datepicker.js"
></script>
...
...
templates/jperm/role_sudo.j2
View file @
2691cc0b
...
...
@@ -46,7 +46,7 @@ add_role_chosen() {
check_syntax
(){
visudo
-c
-f
$1
/usr/sbin/
visudo
-c
-f
$1
}
cp
$real_file
$tmp_file
&&
add_cmd_alias
$tmp_file
&&
add_role_chosen
$tmp_file
||
exit
1
...
...
templates/juser/user_add.html
View file @
2691cc0b
...
...
@@ -131,7 +131,7 @@ $('#userForm').validator({
timely
:
2
,
theme
:
"yellow_right_effect"
,
rules
:
{
check_username
:
[
/^
\w{3,20}
$/
,
'大小写字母数字和下划线
'
],
check_username
:
[
/^
[\w
.
]{3,20}
$/
,
'大小写字母数字和下划线小数点
'
],
type_m
:
function
(
element
){
return
$
(
"#M"
).
is
(
":checked"
);
}
...
...
templates/upload.html
View file @
2691cc0b
...
...
@@ -99,6 +99,7 @@
autoProcessQueue
:
false
,
uploadMultiple
:
true
,
parallelUploads
:
100
,
maxFilesize
:
2048
,
maxFiles
:
100
,
url
:
'/file/upload/'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment