Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
C
coco
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
coco
Commits
433574ce
Unverified
Commit
433574ce
authored
Jan 26, 2018
by
liuzheng712
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: publish
parent
754028bb
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
80 additions
and
126 deletions
+80
-126
.gitignore
.gitignore
+0
-2
httpd.py
coco/httpd.py
+47
-30
models.py
coco/models.py
+6
-1
recorder.py
coco/recorder.py
+24
-2
utils.py
coco/utils.py
+0
-90
requirements.txt
requirements/requirements.txt
+3
-1
No files found.
.gitignore
View file @
433574ce
...
...
@@ -8,5 +8,3 @@ logs/*
conf.py
host_rsa_key
sessions/*
Dockerfile
conf_docker.py
coco/httpd.py
View file @
433574ce
...
...
@@ -20,6 +20,9 @@ logger = get_logger(__file__)
class
BaseWebSocketHandler
:
clients
=
None
current_user
=
None
def
app
(
self
,
app
):
self
.
app
=
app
return
self
...
...
@@ -31,11 +34,13 @@ class BaseWebSocketHandler:
remote_ip
=
x_forwarded_for
[
0
]
else
:
remote_ip
=
request
.
remote_addr
self
.
clients
[
request
.
sid
][
"request"
]
=
Request
((
remote_ip
,
0
))
self
.
clients
[
request
.
sid
][
"request"
]
.
user
=
self
.
current_user
self
.
clients
[
request
.
sid
][
"request"
]
.
meta
=
{
"width"
:
self
.
clients
[
request
.
sid
][
"cols"
],
"height"
:
self
.
clients
[
request
.
sid
][
"rows"
]}
# self.request.__dict__.update(request.__dict__)
req
=
Request
((
remote_ip
,
0
))
req
.
user
=
self
.
current_user
req
.
meta
=
{
"width"
:
self
.
clients
[
request
.
sid
][
"cols"
],
"height"
:
self
.
clients
[
request
.
sid
][
"rows"
]
}
self
.
clients
[
request
.
sid
][
"request"
]
=
req
def
check_origin
(
self
,
origin
):
return
True
...
...
@@ -45,7 +50,6 @@ class BaseWebSocketHandler:
self
.
clients
[
request
.
sid
][
"client"
]
.
close
()
except
:
pass
pass
class
SSHws
(
Namespace
,
BaseWebSocketHandler
):
...
...
@@ -72,8 +76,10 @@ class SSHws(Namespace, BaseWebSocketHandler):
"rw"
:
[]
}
join_room
(
room
)
self
.
current_user
=
self
.
app
.
service
.
check_user_cookie
(
session_id
=
request
.
cookies
.
get
(
'sessionid'
,
''
),
csrf_token
=
request
.
cookies
.
get
(
'csrftoken'
,
''
))
self
.
current_user
=
self
.
app
.
service
.
check_user_cookie
(
session_id
=
request
.
cookies
.
get
(
'sessionid'
,
''
),
csrf_token
=
request
.
cookies
.
get
(
'csrftoken'
,
''
)
)
self
.
prepare
(
request
)
def
on_data
(
self
,
message
):
...
...
@@ -83,25 +89,31 @@ class SSHws(Namespace, BaseWebSocketHandler):
def
on_host
(
self
,
message
):
# 此处获取主机的信息
connection
=
str
(
uuid
.
uuid4
())
asset
ID
=
message
.
get
(
'uuid'
,
None
)
userid
=
message
.
get
(
'userid'
,
None
)
asset
_id
=
message
.
get
(
'uuid'
,
None
)
user
_
id
=
message
.
get
(
'userid'
,
None
)
self
.
emit
(
'room'
,
{
'room'
:
connection
,
'secret'
:
message
[
'secret'
]})
if
assetID
and
userid
:
asset
=
self
.
app
.
service
.
get_asset
(
assetID
)
system_user
=
self
.
app
.
service
.
get_system_user
(
userid
)
if
system_user
:
if
asset_id
and
user_id
:
asset
=
self
.
app
.
service
.
get_asset
(
asset_id
)
system_user
=
self
.
app
.
service
.
get_system_user
(
user_id
)
if
system_user
:
child
,
parent
=
socket
.
socketpair
()
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
=
Client
(
parent
,
self
.
clients
[
request
.
sid
][
"request"
])
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
=
WSProxy
(
self
,
child
,
self
.
clients
[
request
.
sid
][
"room"
],
connection
)
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
=
Client
(
parent
,
self
.
clients
[
request
.
sid
][
"request"
]
)
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
=
WSProxy
(
self
,
child
,
self
.
clients
[
request
.
sid
][
"room"
],
connection
)
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
=
ProxyServer
(
self
.
app
,
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
)
self
.
app
.
clients
.
append
(
self
.
clients
[
request
.
sid
][
"client"
][
connection
])
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
=
ProxyServer
(
self
.
app
,
self
.
clients
[
request
.
sid
][
"client"
][
connection
])
self
.
socketio
.
start_background_task
(
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
.
proxy
,
asset
,
system_user
)
self
.
socketio
.
start_background_task
(
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
.
proxy
,
asset
,
system_user
)
# self.forwarder.proxy(self.asset, system_user)
else
:
self
.
on_disconnect
()
...
...
@@ -135,20 +147,25 @@ class SSHws(Namespace, BaseWebSocketHandler):
def
on_disconnect
(
self
):
self
.
on_leave
(
self
.
clients
[
request
.
sid
][
"room"
])
try
:
for
connection
in
self
.
clients
[
request
.
sid
][
"client"
]:
self
.
on_logout
(
connection
)
del
self
.
clients
[
request
.
sid
]
except
:
pass
# self.ssh.close()
pass
def
on_logout
(
self
,
connection
):
print
(
"logout"
,
connection
)
if
connection
:
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
.
close
()
del
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
del
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
.
close
()
del
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
if
connection
in
self
.
clients
[
request
.
sid
][
"proxy"
]
.
keys
():
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
.
close
()
def
logout
(
self
,
connection
):
if
connection
and
(
request
.
sid
in
self
.
clients
.
keys
()):
if
connection
in
self
.
clients
[
request
.
sid
][
"proxy"
]
.
keys
():
del
self
.
clients
[
request
.
sid
][
"proxy"
][
connection
]
if
connection
in
self
.
clients
[
request
.
sid
][
"forwarder"
]
.
keys
():
del
self
.
clients
[
request
.
sid
][
"forwarder"
][
connection
]
if
connection
in
self
.
clients
[
request
.
sid
][
"client"
]
.
keys
():
del
self
.
clients
[
request
.
sid
][
"client"
][
connection
]
class
HttpServer
:
...
...
coco/models.py
View file @
433574ce
...
...
@@ -212,7 +212,10 @@ class WSProxy:
def
forward
(
self
):
while
not
self
.
stop_event
.
is_set
():
data
=
self
.
child
.
recv
(
BUF_SIZE
)
try
:
data
=
self
.
child
.
recv
(
BUF_SIZE
)
except
OSError
:
continue
if
len
(
data
)
==
0
:
self
.
close
()
self
.
ws
.
emit
(
"data"
,
{
'data'
:
data
.
decode
(
"utf-8"
),
'room'
:
self
.
connection
},
room
=
self
.
room
)
...
...
@@ -225,3 +228,5 @@ class WSProxy:
def
close
(
self
):
self
.
stop_event
.
set
()
self
.
child
.
close
()
self
.
ws
.
logout
(
self
.
connection
)
logger
.
debug
(
"Proxy {} closed"
.
format
(
self
))
coco/recorder.py
View file @
433574ce
...
...
@@ -9,6 +9,7 @@ import os
import
gzip
import
json
import
shutil
import
boto3
# AWS S3 sdk
from
jms_es_sdk
import
ESStore
...
...
@@ -238,6 +239,25 @@ class ESCommandRecorder(CommandRecorder, metaclass=Singleton):
print
(
"{} has been gc"
.
format
(
self
))
class
S3ReplayRecorder
(
ServerReplayRecorder
):
def
__init__
(
self
,
app
):
super
()
.
__init__
(
app
)
self
.
bucket
=
app
.
config
[
"REPLAY_STORAGE"
]
.
get
(
"BUCKET"
,
"jumpserver"
)
self
.
s3
=
boto3
.
client
(
's3'
)
def
push_to_server
(
self
,
session_id
):
try
:
self
.
s3
.
upload_file
(
os
.
path
.
join
(
self
.
app
.
config
[
'LOG_DIR'
],
session_id
+
'.replay.gz'
),
self
.
bucket
,
time
.
strftime
(
'
%
Y-
%
m-
%
d'
,
time
.
localtime
(
self
.
starttime
))
+
'/'
+
session_id
+
'.replay.gz'
)
except
:
return
self
.
app
.
service
.
push_session_replay
(
os
.
path
.
join
(
self
.
app
.
config
[
'LOG_DIR'
],
session_id
+
'.replay.gz'
),
session_id
)
def
get_command_recorder_class
(
config
):
command_storage
=
config
[
"COMMAND_STORAGE"
]
...
...
@@ -248,8 +268,10 @@ def get_command_recorder_class(config):
def
get_replay_recorder_class
(
config
):
replay_engine
=
config
[
"REPLAY_RECORD_ENGINE"
]
if
replay_engine
==
"server"
:
replay_storage
=
config
[
"REPLAY_STORAGE"
]
if
replay_storage
[
'TYPE'
]
==
"s3"
:
return
S3ReplayRecorder
elif
replay_storage
[
'TYPE'
]
==
"locale"
:
return
ServerReplayRecorder
else
:
return
ServerReplayRecorder
coco/utils.py
View file @
433574ce
...
...
@@ -86,61 +86,6 @@ def ssh_key_gen(length=2048, type='rsa', password=None,
raise
IOError
(
'These is error when generate ssh key.'
)
def
content_md5
(
data
):
"""计算data的MD5值,经过Base64编码并返回str类型。
返回值可以直接作为HTTP Content-Type头部的值
"""
if
isinstance
(
data
,
str
):
data
=
hashlib
.
md5
(
data
.
encode
(
'utf-8'
))
value
=
base64
.
b64encode
(
data
.
digest
())
return
value
.
decode
(
'utf-8'
)
_STRPTIME_LOCK
=
threading
.
Lock
()
_GMT_FORMAT
=
"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"
_ISO8601_FORMAT
=
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.000Z"
def
to_unixtime
(
time_string
,
format_string
):
with
_STRPTIME_LOCK
:
return
int
(
calendar
.
timegm
(
time
.
strptime
(
str
(
time_string
),
format_string
)))
def
http_date
(
timeval
=
None
):
"""返回符合HTTP标准的GMT时间字符串,用strftime的格式表示就是"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"。
但不能使用strftime,因为strftime的结果是和locale相关的。
"""
return
formatdate
(
timeval
,
usegmt
=
True
)
def
http_to_unixtime
(
time_string
):
"""把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。
HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。
"""
return
to_unixtime
(
time_string
,
_GMT_FORMAT
)
def
iso8601_to_unixtime
(
time_string
):
"""把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。"""
return
to_unixtime
(
time_string
,
_ISO8601_FORMAT
)
def
make_signature
(
access_key_secret
,
date
=
None
):
if
isinstance
(
date
,
bytes
):
date
=
bytes
.
decode
(
date
)
if
isinstance
(
date
,
int
):
date_gmt
=
http_date
(
date
)
elif
date
is
None
:
date_gmt
=
http_date
(
int
(
time
.
time
()))
else
:
date_gmt
=
date
data
=
str
(
access_key_secret
)
+
"
\n
"
+
date_gmt
return
content_md5
(
data
)
class
TtyIOParser
(
object
):
def
__init__
(
self
,
width
=
80
,
height
=
24
):
self
.
screen
=
pyte
.
Screen
(
width
,
height
)
...
...
@@ -287,10 +232,6 @@ def wrap_with_title(text):
return
wrap_with_color
(
text
,
color
=
'black'
,
background
=
'green'
)
def
b64encode_as_string
(
data
):
return
base64
.
b64encode
(
data
)
.
decode
(
"utf-8"
)
def
split_string_int
(
s
):
"""Split string or int
...
...
@@ -324,37 +265,6 @@ def sort_assets(assets, order_by='hostname'):
return
assets
class
PKey
(
object
):
@classmethod
def
from_string
(
cls
,
key_string
):
try
:
pkey
=
paramiko
.
RSAKey
(
file_obj
=
StringIO
(
key_string
))
return
pkey
except
paramiko
.
SSHException
:
try
:
pkey
=
paramiko
.
DSSKey
(
file_obj
=
StringIO
(
key_string
))
return
pkey
except
paramiko
.
SSHException
:
return
None
def
timestamp_to_datetime_str
(
ts
):
datetime_format
=
'
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.
%
fZ'
dt
=
datetime
.
datetime
.
fromtimestamp
(
ts
,
tz
=
pytz
.
timezone
(
'UTC'
))
return
dt
.
strftime
(
datetime_format
)
class
MultiQueue
(
Queue
):
def
mget
(
self
,
size
=
1
,
block
=
True
,
timeout
=
5
):
items
=
[]
for
i
in
range
(
size
):
try
:
items
.
append
(
self
.
get
(
block
=
block
,
timeout
=
timeout
))
except
Empty
:
break
return
items
def
_gettext
():
gettext
.
bindtextdomain
(
"coco"
,
os
.
path
.
join
(
BASE_DIR
,
"locale"
))
gettext
.
textdomain
(
"coco"
)
...
...
requirements/requirements.txt
View file @
433574ce
asn1crypto==0.23.0
bcrypt==3.1.4
boto3==1.5.18
botocore==1.8.32
certifi==2017.11.5
cffi==1.11.2
chardet==3.0.4
...
...
@@ -28,5 +30,5 @@ tornado==4.5.2
urllib3==1.22
wcwidth==0.1.7
werkzeug==0.12.2
jumpserver-python-sdk==0.0.2
6
jumpserver-python-sdk==0.0.2
7
jms-es-sdk
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