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
15b74da5
Unverified
Commit
15b74da5
authored
Mar 21, 2018
by
老广
Committed by
GitHub
Mar 21, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1100 from jumpserver/dev
Dev
parents
227804b7
0bba840e
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
451 additions
and
209 deletions
+451
-209
__init__.py
apps/__init__.py
+1
-1
user.py
apps/assets/forms/user.py
+1
-9
asset_list.html
apps/assets/templates/assets/asset_list.html
+1
-0
step_by_step.rst
docs/step_by_step.rst
+1
-1
upgrade.rst
docs/upgrade.rst
+118
-5
jms
jms
+323
-0
run_server.py
run_server.py
+4
-151
build_docker.sh
utils/build_docker.sh
+0
-5
export_init_data.sh
utils/export_init_data.sh
+0
-18
init_db.sh
utils/init_db.sh
+0
-4
make_migrations.sh
utils/make_migrations.sh
+2
-2
run_docker.sh
utils/run_docker.sh
+0
-13
No files found.
apps/__init__.py
View file @
15b74da5
...
...
@@ -2,4 +2,4 @@
# -*- coding: utf-8 -*-
#
__version__
=
"
0.5
.0"
__version__
=
"
1.0
.0"
apps/assets/forms/user.py
View file @
15b74da5
...
...
@@ -114,22 +114,15 @@ class SystemUserForm(PasswordAndKeyAuthForm):
fields
=
[
'name'
,
'username'
,
'protocol'
,
'auto_generate_key'
,
'password'
,
'private_key_file'
,
'auto_push'
,
'sudo'
,
'comment'
,
'shell'
,
'
nodes'
,
'
priority'
,
'comment'
,
'shell'
,
'priority'
,
]
widgets
=
{
'name'
:
forms
.
TextInput
(
attrs
=
{
'placeholder'
:
_
(
'Name'
)}),
'username'
:
forms
.
TextInput
(
attrs
=
{
'placeholder'
:
_
(
'Username'
)}),
'nodes'
:
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
'data-placeholder'
:
_
(
'Nodes'
)
}
),
}
help_texts
=
{
'name'
:
'* required'
,
'username'
:
'* required'
,
'nodes'
:
_
(
'If auto push checked, system user will be create at node assets'
),
'auto_push'
:
_
(
'Auto push system user to asset'
),
'priority'
:
_
(
'High level will be using login asset as default, if user was granted more than 2 system user'
),
}
\ No newline at end of file
apps/assets/templates/assets/asset_list.html
View file @
15b74da5
...
...
@@ -655,6 +655,7 @@ $(document).ready(function(){
default
:
break
;
}
$
(
".ipt_check_all"
).
prop
(
"checked"
,
false
)
});
</script>
...
...
docs/step_by_step.rst
View file @
15b74da5
...
...
@@ -224,7 +224,7 @@ Luna 已改为纯前端,需要 Nginx 来运行访问
# 注意:这里一定要改写一下本机的IP地址, 否则会出错
docker run -d \
docker run -
-name jms_guacamole -
d \
-p 8081:8080 -v /opt/guacamole/key:/config/guacamole/key \
-e JUMPSERVER_KEY_DIR=/config/guacamole/key \
-e JUMPSERVER_SERVER=http://<填写本机的IP地址>:8080 \
...
...
docs/upgrade.rst
View file @
15b74da5
升级
----
更新
升级
----
---------
1. 升级 Jumpserver
...
...
@@ -22,8 +22,121 @@
::
$ docker pull registry.jumpserver.org/public/guacamole:latest
$ docker stop <guacamole>
$ docker run -d \
-p 8081:8080 \
$ docker stop jms_guacamole # 或者写guacamole的容器ID
$ docker run --name jms_guacamole -d \
-p 8081:8080 -v /opt/guacamole/key:/config/guacamole/key \
-e JUMPSERVER_KEY_DIR=/config/guacamole/key \
-e JUMPSERVER_SERVER=http://<填写本机的IP地址>:8080 \
registry.jumpserver.org/public/guacamole:latest
切换分支或离线升级
-------------------------------
**Jumpserver**
说明: 以下操作,都在jumpserver所在目录运行
1. 备份配置文件
::
$ jumpserver_backup=/tmp/jumpserver_backup
$ mkdir -p $jumpserver_backup
$ cp config.py $jumpserver_backup
2. 备份migrations migrations中存的是数据库表结构的变更,切换分支会丢失
::
$ for app in common users assets ops perms terminal;do
mkdir -p $jumpserver_backup/${app}_migrations
cp apps/${app}/migrations/*.py $jumpserver_backup/${app}_migrations
done
3. 备份数据库,已被不时之需
::
$ mysqldump -u你的数据库账号 -h数据库地址 -p 数据库名称 > $jumpserver_backup/db_backup.sql
4. 备份录像文件
::
$ cp -r data/media $jumpserver_backup/
5. 切换分支或下载离线包, 更新代码
::
$ git checkout master # or other branch
6. 还原配置文件
::
$ cp $jumpserver_backup/config.py .
7. 还原数据库表结构记录
::
$ for app in common users assets ops perms terminal;do
cp $jumpserver_backup/${app}_migrations/*.py ${app}/migrations/
done
8. 还原录像文件
::
$ cp -r $jumpserver_backup/media/* data/media/
9. 更新依赖或表结构
::
$ pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh
**Coco**
说明: 以下操作都在 coco 项目所在目录
coco是无状态的,备份 keys 目录即可
1. 备份keys
::
$ cp -r keys $jumpserver_backup/
2. 离线更新升级coco
3. 还原 keys目录
::
$ mv keys keys_backup
$ cp -r $jumpserver_backup/keys .
4. 升级依赖
::
$ git pull && cd requirements && pip install -r requirements.txt
**Luna**
直接下载最新Release包替换即可
**Guacamole**
直接参考上面的升级即可, 需要注意的是如果更换机器,请备份
jms
0 → 100755
View file @
15b74da5
#!/usr/bin/env python3
# coding: utf-8
import
os
import
subprocess
import
threading
import
time
import
argparse
import
sys
import
signal
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
sys
.
path
.
append
(
BASE_DIR
)
from
apps
import
__version__
try
:
from
config
import
config
as
CONFIG
except
ImportError
:
print
(
"Could not find config file, `cp config_example.py config.py`"
)
sys
.
exit
(
1
)
os
.
environ
[
"PYTHONIOENCODING"
]
=
"UTF-8"
APPS_DIR
=
os
.
path
.
join
(
BASE_DIR
,
'apps'
)
LOG_DIR
=
os
.
path
.
join
(
BASE_DIR
,
'logs'
)
TMP_DIR
=
os
.
path
.
join
(
BASE_DIR
,
'tmp'
)
HTTP_HOST
=
CONFIG
.
HTTP_BIND_HOST
or
'127.0.0.1'
HTTP_PORT
=
CONFIG
.
HTTP_LISTEN_PORT
or
8080
DEBUG
=
CONFIG
.
DEBUG
LOG_LEVEL
=
CONFIG
.
LOG_LEVEL
START_TIMEOUT
=
15
WORKERS
=
4
DAEMON
=
False
EXIT_EVENT
=
threading
.
Event
()
all_services
=
[
'gunicorn'
,
'celery'
,
'beat'
]
try
:
os
.
makedirs
(
os
.
path
.
join
(
BASE_DIR
,
"data"
,
"static"
))
os
.
makedirs
(
os
.
path
.
join
(
BASE_DIR
,
"data"
,
"media"
))
except
:
pass
def
make_migrations
():
print
(
"Check database structure change ..."
)
os
.
chdir
(
os
.
path
.
join
(
BASE_DIR
,
'apps'
))
subprocess
.
call
(
'python3 manage.py migrate'
,
shell
=
True
)
def
collect_static
():
print
(
"Collect static files"
)
os
.
chdir
(
os
.
path
.
join
(
BASE_DIR
,
'apps'
))
subprocess
.
call
(
'python3 manage.py collectstatic --no-input'
,
shell
=
True
)
def
prepare
():
make_migrations
()
collect_static
()
def
check_pid
(
pid
):
""" Check For the existence of a unix pid. """
try
:
os
.
kill
(
pid
,
0
)
except
OSError
:
return
False
else
:
return
True
def
get_pid_file_path
(
service
):
return
os
.
path
.
join
(
TMP_DIR
,
'{}.pid'
.
format
(
service
))
def
get_log_file_path
(
service
):
return
os
.
path
.
join
(
LOG_DIR
,
'{}.log'
.
format
(
service
))
def
get_pid
(
service
):
pid_file
=
get_pid_file_path
(
service
)
if
os
.
path
.
isfile
(
pid_file
):
with
open
(
pid_file
)
as
f
:
return
int
(
f
.
read
()
.
strip
())
return
0
def
is_running
(
s
,
unlink
=
True
):
pid_file
=
get_pid_file_path
(
s
)
if
os
.
path
.
isfile
(
pid_file
):
with
open
(
pid_file
,
'r'
)
as
f
:
pid
=
get_pid
(
s
)
if
check_pid
(
pid
):
return
True
if
unlink
:
os
.
unlink
(
pid_file
)
return
False
def
parse_service
(
s
):
if
s
==
'all'
:
return
all_services
else
:
return
[
s
]
def
start_gunicorn
():
print
(
"
\n
- Start Gunicorn WSGI HTTP Server"
)
prepare
()
service
=
'gunicorn'
bind
=
'{}:{}'
.
format
(
HTTP_HOST
,
HTTP_PORT
)
log_format
=
'
%(h)
s
%(t)
s "
%(r)
s"
%(s)
s
%(b)
s '
pid_file
=
get_pid_file_path
(
service
)
log_file
=
get_log_file_path
(
service
)
cmd
=
[
'gunicorn'
,
'jumpserver.wsgi'
,
'-b'
,
bind
,
'-w'
,
str
(
WORKERS
),
'--access-logformat'
,
log_format
,
'-p'
,
pid_file
,
]
if
DAEMON
:
cmd
.
extend
([
'--access-logfile'
,
log_file
,
'--daemon'
,
])
else
:
cmd
.
extend
([
'--access-logfile'
,
'-'
])
if
DEBUG
:
cmd
.
append
(
'--reload'
)
p
=
subprocess
.
Popen
(
cmd
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,
cwd
=
APPS_DIR
)
return
p
def
start_celery
():
print
(
"
\n
- Start Celery as Distributed Task Queue"
)
# Todo: Must set this environment, otherwise not no ansible result return
os
.
environ
.
setdefault
(
'PYTHONOPTIMIZE'
,
'1'
)
if
os
.
getuid
()
==
0
:
os
.
environ
.
setdefault
(
'C_FORCE_ROOT'
,
'1'
)
service
=
'celery'
pid_file
=
get_pid_file_path
(
service
)
cmd
=
[
'celery'
,
'worker'
,
'-A'
,
'common'
,
'-l'
,
LOG_LEVEL
.
lower
(),
'--pidfile'
,
pid_file
,
'-c'
,
str
(
WORKERS
),
]
if
DAEMON
:
cmd
.
extend
([
'--logfile'
,
os
.
path
.
join
(
LOG_DIR
,
'celery.log'
),
'--detach'
,
])
p
=
subprocess
.
Popen
(
cmd
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,
cwd
=
APPS_DIR
)
return
p
def
start_beat
():
print
(
"
\n
- Start Beat as Periodic Task Scheduler"
)
pid_file
=
get_pid_file_path
(
'beat'
)
log_file
=
get_log_file_path
(
'beat'
)
os
.
environ
.
setdefault
(
'PYTHONOPTIMIZE'
,
'1'
)
if
os
.
getuid
()
==
0
:
os
.
environ
.
setdefault
(
'C_FORCE_ROOT'
,
'1'
)
scheduler
=
"django_celery_beat.schedulers:DatabaseScheduler"
cmd
=
[
'celery'
,
'beat'
,
'-A'
,
'common'
,
'--pidfile'
,
pid_file
,
'-l'
,
LOG_LEVEL
,
'--scheduler'
,
scheduler
,
'--max-interval'
,
'60'
]
if
DAEMON
:
cmd
.
extend
([
'--logfile'
,
log_file
,
'--detach'
,
])
p
=
subprocess
.
Popen
(
cmd
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,
cwd
=
APPS_DIR
)
return
p
def
start_service
(
s
):
print
(
time
.
ctime
())
print
(
'Jumpserver version {}, more see https://www.jumpserver.org'
.
format
(
__version__
))
services_handler
=
{
"gunicorn"
:
start_gunicorn
,
"celery"
:
start_celery
,
"beat"
:
start_beat
}
services_set
=
parse_service
(
s
)
processes
=
[]
for
i
in
services_set
:
if
is_running
(
i
):
show_service_status
(
i
)
continue
func
=
services_handler
.
get
(
i
)
p
=
func
()
processes
.
append
(
p
)
now
=
int
(
time
.
time
())
for
i
in
services_set
:
while
not
is_running
(
i
):
if
int
(
time
.
time
())
-
now
<
START_TIMEOUT
:
time
.
sleep
(
1
)
continue
else
:
print
(
"Error: {} start error"
.
format
(
i
))
stop_multi_services
(
services_set
)
return
stop_event
=
threading
.
Event
()
if
not
DAEMON
:
signal
.
signal
(
signal
.
SIGTERM
,
lambda
x
,
y
:
stop_event
.
set
())
while
not
stop_event
.
is_set
():
try
:
time
.
sleep
(
10
)
except
KeyboardInterrupt
:
stop_event
.
set
()
break
print
(
"Stop services"
)
for
p
in
processes
:
p
.
terminate
()
for
i
in
services_set
:
stop_service
(
i
)
else
:
print
()
show_service_status
(
s
)
def
stop_service
(
s
,
sig
=
15
):
services_set
=
parse_service
(
s
)
for
s
in
services_set
:
if
not
is_running
(
s
):
show_service_status
(
s
)
continue
print
(
"Stop service: {}"
.
format
(
s
))
pid
=
get_pid
(
s
)
os
.
kill
(
pid
,
sig
)
def
stop_multi_services
(
services
):
for
s
in
services
:
stop_service
(
s
,
sig
=
9
)
def
stop_service_force
(
s
):
stop_service
(
s
,
sig
=
9
)
def
show_service_status
(
s
):
services_set
=
parse_service
(
s
)
for
ns
in
services_set
:
if
is_running
(
ns
):
pid
=
get_pid
(
ns
)
print
(
"{} is running: {}"
.
format
(
ns
,
pid
))
else
:
print
(
"{} is stopped"
.
format
(
ns
))
if
__name__
==
'__main__'
:
parser
=
argparse
.
ArgumentParser
(
description
=
"""
Jumpserver service control tools;
Example:
\r\n
%(prog)
s start all -d;
"""
)
parser
.
add_argument
(
'action'
,
type
=
str
,
choices
=
(
"start"
,
"stop"
,
"restart"
,
"status"
),
help
=
"Action to run"
)
parser
.
add_argument
(
"service"
,
type
=
str
,
default
=
"all"
,
nargs
=
"?"
,
choices
=
(
"all"
,
"gunicorn"
,
"celery"
,
"beat"
),
help
=
"The service to start"
,
)
parser
.
add_argument
(
'-d'
,
'--daemon'
,
nargs
=
"?"
,
const
=
1
)
parser
.
add_argument
(
'-w'
,
'--worker'
,
type
=
int
,
nargs
=
"?"
,
const
=
4
)
args
=
parser
.
parse_args
()
if
args
.
daemon
:
DAEMON
=
True
if
args
.
worker
:
WORKERS
=
args
.
worker
action
=
args
.
action
srv
=
args
.
service
if
action
==
"start"
:
start_service
(
srv
)
elif
action
==
"stop"
:
stop_service
(
srv
)
elif
action
==
"restart"
:
DAEMON
=
True
stop_service
(
srv
)
time
.
sleep
(
5
)
start_service
(
srv
)
else
:
show_service_status
(
srv
)
run_server.py
View file @
15b74da5
#!/usr/bin/env python
#!/usr/bin/env python
3
# coding: utf-8
import
os
import
subprocess
import
threading
import
time
import
argparse
import
sys
from
apps
import
__version__
try
:
from
config
import
config
as
CONFIG
except
ImportError
:
CONFIG
=
type
(
'_'
,
(),
{
'__getattr__'
:
lambda
*
arg
:
None
})()
os
.
environ
[
"PYTHONIOENCODING"
]
=
"UTF-8"
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
APPS_DIR
=
os
.
path
.
join
(
BASE_DIR
,
'apps'
)
HTTP_HOST
=
CONFIG
.
HTTP_BIND_HOST
or
'127.0.0.1'
HTTP_PORT
=
CONFIG
.
HTTP_LISTEN_PORT
or
8080
DEBUG
=
CONFIG
.
DEBUG
LOG_LEVEL
=
CONFIG
.
LOG_LEVEL
WORKERS
=
4
EXIT_EVENT
=
threading
.
Event
()
processes
=
{}
try
:
os
.
makedirs
(
os
.
path
.
join
(
BASE_DIR
,
"data"
,
"static"
))
os
.
makedirs
(
os
.
path
.
join
(
BASE_DIR
,
"data"
,
"media"
))
except
:
pass
def
make_migrations
():
print
(
"Check database change, make migrations"
)
os
.
chdir
(
os
.
path
.
join
(
BASE_DIR
,
'apps'
))
subprocess
.
call
(
'python manage.py migrate'
,
shell
=
True
)
def
collect_static
():
print
(
"Collect static files"
)
os
.
chdir
(
os
.
path
.
join
(
BASE_DIR
,
'apps'
))
subprocess
.
call
(
'python manage.py collectstatic --no-input'
,
shell
=
True
)
def
start_gunicorn
():
print
(
"- Start Gunicorn WSGI HTTP Server"
)
make_migrations
()
collect_static
()
os
.
chdir
(
APPS_DIR
)
cmd
=
"gunicorn jumpserver.wsgi -b {}:{} -w {} "
.
format
(
HTTP_HOST
,
HTTP_PORT
,
WORKERS
)
log_format
=
'
%(h)
s
%(t)
s "
%(r)
s"
%(s)
s
%(b)
s '
log
=
" --access-logfile - --access-logformat '{}' "
.
format
(
log_format
)
cmd
+=
log
if
DEBUG
:
cmd
+=
" --reload"
p
=
subprocess
.
Popen
(
cmd
,
shell
=
True
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
)
return
p
def
start_celery
():
print
(
"- Start Celery as Distributed Task Queue"
)
os
.
chdir
(
APPS_DIR
)
# Todo: Must set this environment, otherwise not no ansible result return
os
.
environ
.
setdefault
(
'PYTHONOPTIMIZE'
,
'1'
)
cmd
=
"""
export C_FORCE_ROOT=1;
celery -A common worker -l {}
"""
.
format
(
LOG_LEVEL
.
lower
())
p
=
subprocess
.
Popen
(
cmd
,
shell
=
True
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
)
return
p
def
start_beat
():
print
(
"- Start Beat as Periodic Task Scheduler"
)
os
.
chdir
(
APPS_DIR
)
os
.
environ
.
setdefault
(
'PYTHONOPTIMIZE'
,
'1'
)
os
.
environ
.
setdefault
(
'C_FORCE_ROOT'
,
'1'
)
pidfile
=
'/tmp/beat.pid'
if
os
.
path
.
exists
(
pidfile
):
print
(
"Beat pid file `{}` exist, remove it"
.
format
(
pidfile
))
os
.
unlink
(
pidfile
)
time
.
sleep
(
0.5
)
if
os
.
path
.
exists
(
pidfile
):
print
(
"Beat pid file `{}` exist yet, may be something wrong"
.
format
(
pidfile
))
os
.
unlink
(
pidfile
)
time
.
sleep
(
0.5
)
scheduler
=
"django_celery_beat.schedulers:DatabaseScheduler"
options
=
"--pidfile {} -l {} --scheduler {} --max-interval 60"
.
format
(
pidfile
,
LOG_LEVEL
,
scheduler
,
)
cmd
=
'celery -A common beat {} '
.
format
(
options
)
p
=
subprocess
.
Popen
(
cmd
,
shell
=
True
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
)
return
p
def
start_service
(
services
):
print
(
time
.
ctime
())
print
(
'Jumpserver version {}, more see https://www.jumpserver.org'
.
format
(
__version__
))
print
(
'Quit the server with CONTROL-C.'
)
services_all
=
{
"gunicorn"
:
start_gunicorn
,
"celery"
:
start_celery
,
"beat"
:
start_beat
}
if
'all'
in
services
:
for
name
,
func
in
services_all
.
items
():
processes
[
name
]
=
func
()
else
:
for
name
in
services
:
func
=
services_all
.
get
(
name
)
processes
[
name
]
=
func
()
stop_event
=
threading
.
Event
()
while
not
stop_event
.
is_set
():
for
name
,
proc
in
processes
.
items
():
if
proc
.
poll
()
is
not
None
:
print
(
"
\n\n
"
+
"####"
*
10
+
" ERROR OCCUR "
+
"####"
*
10
)
print
(
"Start service {} [FAILED]"
.
format
(
name
))
for
_
,
p
in
processes
.
items
():
p
.
terminate
()
stop_event
.
set
()
print
(
"Exited"
.
format
(
name
))
break
time
.
sleep
(
5
)
def
stop_service
():
for
name
,
proc
in
processes
.
items
():
print
(
"Stop service {}"
.
format
(
name
))
proc
.
terminate
()
if
os
.
path
.
exists
(
"/tmp/beat.pid"
):
os
.
unlink
(
'/tmp/beat.pid'
)
import
subprocess
if
__name__
==
'__main__'
:
parser
=
argparse
.
ArgumentParser
(
description
=
"Jumpserver start tools"
)
parser
.
add_argument
(
"services"
,
type
=
str
,
nargs
=
'+'
,
default
=
"all"
,
choices
=
(
"all"
,
"gunicorn"
,
"celery"
,
"beat"
),
help
=
"The service to start"
,
)
args
=
parser
.
parse_args
()
start_service
(
args
.
services
)
subprocess
.
call
(
'python3 jms start all'
,
shell
=
True
,
stdin
=
sys
.
stdin
,
stdout
=
sys
.
stdout
)
utils/build_docker.sh
deleted
100755 → 0
View file @
227804b7
#!/bin/bash
#
cd
..
docker build
-t
jumpserver/jumpserver:v0.4.0-beta1
.
utils/export_init_data.sh
deleted
100644 → 0
View file @
227804b7
#!/bin/bash
#
python ../apps/manage.py shell
<<
EOF
from users.models import *
init_model()
from assets.models import *
init_model()
EOF
python ../apps/manage.py dbshell
<<
EOF
delete from auth_permission;
delete from django_content_type;
EOF
python ../apps/manage.py dumpdata
>
../apps/fixtures/init.json
utils/init_db.sh
deleted
100755 → 0
View file @
227804b7
#!/bin/bash
#
python ../apps/manage.py loaddata init
utils/make_migrations.sh
View file @
15b74da5
#!/bin/bash
#
python ../apps/manage.py makemigrations
python
3
../apps/manage.py makemigrations
python ../apps/manage.py migrate
python
3
../apps/manage.py migrate
utils/run_docker.sh
deleted
100755 → 0
View file @
227804b7
#!/bin/bash
#
# Run redis
docker run
--name
redis
-d
redis
# Run jumpserver
docker run
-d
--name
jumpserver
-p
8080:8080
--link
redis:redis jumpserver/jumpserver:v0.4.0-beta1
# Finished
echo
-e
"Please visit http://ServerIP:8080
\n
Username: admin
\n
Password: admin
\n
"
\ No newline at end of file
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