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
09e86f0a
Commit
09e86f0a
authored
9 years ago
by
yumaojun
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of
https://github.com/jumpserver/jumpserver
into dev
parents
3b5daf19
9fa7f876
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 @
09e86f0a
...
...
@@ -37,6 +37,7 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject
.settings
*.log
logs/*
keys/*
...
...
This diff is collapsed.
Click to expand it.
README.md
View file @
09e86f0a
## 写在前面
-
目前本版本处于beta阶段,请不要用于生产环境,除非你知道你在做什么
-
本版本暂时没加入LDAP接口,稳定版会将LDAP和无Agent方式抽象成API,2.x版本支持LDAP,请移步release中下载
#欢迎使用Jumpserver
**Jumpserver**
是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能。基于ssh协议来管理,客户端无需安装agent。
支持常见系统:
...
...
This diff is collapsed.
Click to expand it.
connect.py
View file @
09e86f0a
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
install/install.py
View file @
09e86f0a
...
...
@@ -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
):
...
...
This diff is collapsed.
Click to expand it.
install/next.py
View file @
09e86f0a
...
...
@@ -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
()
...
...
This diff is collapsed.
Click to expand it.
install/zzjumpserver.sh
0 → 100644
View file @
09e86f0a
#!/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
This diff is collapsed.
Click to expand it.
jasset/asset_api.py
View file @
09e86f0a
...
...
@@ -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
:
...
...
This diff is collapsed.
Click to expand it.
jasset/models.py
View file @
09e86f0a
...
...
@@ -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"系统平台"
)
...
...
This diff is collapsed.
Click to expand it.
jasset/views.py
View file @
09e86f0a
...
...
@@ -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
)
...
...
This diff is collapsed.
Click to expand it.
jlog/log_api.py
View file @
09e86f0a
...
...
@@ -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
()
This diff is collapsed.
Click to expand it.
jlog/views.py
View file @
09e86f0a
...
...
@@ -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
))
...
...
This diff is collapsed.
Click to expand it.
jperm/ansible_api.py
View file @
09e86f0a
...
...
@@ -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]
...
...
This diff is collapsed.
Click to expand it.
jperm/views.py
View file @
09e86f0a
...
...
@@ -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
)
...
...
This diff is collapsed.
Click to expand it.
jumpserver/api.py
View file @
09e86f0a
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
jumpserver/settings.py
View file @
09e86f0a
...
...
@@ -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'
),
]
This diff is collapsed.
Click to expand it.
jumpserver/views.py
View file @
09e86f0a
...
...
@@ -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
())
This diff is collapsed.
Click to expand it.
juser/user_api.py
View file @
09e86f0a
...
...
@@ -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
)
...
...
This diff is collapsed.
Click to expand it.
juser/views.py
View file @
09e86f0a
...
...
@@ -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
())
...
...
This diff is collapsed.
Click to expand it.
run_websocket.py
View file @
09e86f0a
...
...
@@ -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
:
...
...
This diff is collapsed.
Click to expand it.
templates/exec_cmd.html
View file @
09e86f0a
...
...
@@ -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
)
{
...
...
This diff is collapsed.
Click to expand it.
templates/jasset/asset_cu_list.html
View file @
09e86f0a
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
templates/jasset/asset_list.html
View file @
09e86f0a
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
templates/jlog/log_online.html
View file @
09e86f0a
...
...
@@ -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>'
);
...
...
This diff is collapsed.
Click to expand it.
templates/jlog/web_terminal.html
View file @
09e86f0a
...
...
@@ -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
};
...
...
This diff is collapsed.
Click to expand it.
templates/jperm/perm_role_add.html
View file @
09e86f0a
...
...
@@ -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
();
...
...
This diff is collapsed.
Click to expand it.
templates/jperm/perm_role_edit.html
View file @
09e86f0a
...
...
@@ -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
();
...
...
This diff is collapsed.
Click to expand it.
templates/jperm/perm_role_list.html
View file @
09e86f0a
...
...
@@ -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>
...
...
This diff is collapsed.
Click to expand it.
templates/jperm/perm_sudo_edit.html
View file @
09e86f0a
...
...
@@ -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>
...
...
This diff is collapsed.
Click to expand it.
templates/jperm/role_sudo.j2
View file @
09e86f0a
...
...
@@ -51,7 +51,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
...
...
This diff is collapsed.
Click to expand it.
templates/juser/user_add.html
View file @
09e86f0a
...
...
@@ -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"
);
}
...
...
This diff is collapsed.
Click to expand it.
templates/upload.html
View file @
09e86f0a
...
...
@@ -99,6 +99,7 @@
autoProcessQueue
:
false
,
uploadMultiple
:
true
,
parallelUploads
:
100
,
maxFilesize
:
2048
,
maxFiles
:
100
,
url
:
'/file/upload/'
,
...
...
This diff is collapsed.
Click to expand it.
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