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
e9a6daa7
Commit
e9a6daa7
authored
Nov 20, 2017
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Update] 修改日志记录选项
parent
eb810d14
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
186 additions
and
127 deletions
+186
-127
app.py
coco/app.py
+44
-30
forward.py
coco/forward.py
+18
-6
httpd.py
coco/httpd.py
+3
-0
interactive.py
coco/interactive.py
+7
-9
models.py
coco/models.py
+26
-13
record.py
coco/record.py
+60
-2
session.py
coco/session.py
+21
-59
sshd.py
coco/sshd.py
+7
-8
No files found.
coco/app.py
View file @
e9a6daa7
...
@@ -9,7 +9,6 @@ from .config import Config
...
@@ -9,7 +9,6 @@ from .config import Config
from
.sshd
import
SSHServer
from
.sshd
import
SSHServer
from
.httpd
import
HttpServer
from
.httpd
import
HttpServer
from
.logging
import
create_logger
from
.logging
import
create_logger
from
.
import
utils
__version__
=
'0.4.0'
__version__
=
'0.4.0'
...
@@ -34,6 +33,7 @@ class Coco:
...
@@ -34,6 +33,7 @@ class Coco:
'LOG_LEVEL'
:
'INFO'
,
'LOG_LEVEL'
:
'INFO'
,
'LOG_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'logs'
),
'LOG_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'logs'
),
'SESSION_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'sessions'
),
'SESSION_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'sessions'
),
'SESSION_COMMAND_STORE'
:
"server"
,
# elasticsearch
'ASSET_LIST_SORT_BY'
:
'hostname'
,
# hostname, ip
'ASSET_LIST_SORT_BY'
:
'hostname'
,
# hostname, ip
'SSH_PASSWORD_AUTH'
:
True
,
'SSH_PASSWORD_AUTH'
:
True
,
'SSH_PUBLIC_KEY_AUTH'
:
True
,
'SSH_PUBLIC_KEY_AUTH'
:
True
,
...
@@ -42,32 +42,37 @@ class Coco:
...
@@ -42,32 +42,37 @@ class Coco:
}
}
def
__init__
(
self
,
name
=
None
,
root_path
=
None
):
def
__init__
(
self
,
name
=
None
,
root_path
=
None
):
self
.
config
=
self
.
config_class
(
BASE_DIR
,
defaults
=
self
.
default_config
)
self
.
root_path
=
root_path
if
root_path
else
BASE_DIR
self
.
config
=
self
.
config_class
(
self
.
root_path
,
defaults
=
self
.
default_config
)
self
.
name
=
name
if
name
else
self
.
config
[
'NAME'
]
self
.
sessions
=
[]
self
.
sessions
=
[]
self
.
clients
=
[]
self
.
clients
=
[]
self
.
root_path
=
root_path
self
.
name
=
name
self
.
lock
=
threading
.
Lock
()
self
.
lock
=
threading
.
Lock
()
self
.
stop_evt
=
threading
.
Event
()
self
.
stop_evt
=
threading
.
Event
()
self
.
service
=
None
if
name
is
None
:
@property
self
.
name
=
self
.
config
[
'NAME'
]
def
service
(
self
):
if
root_path
is
None
:
return
AppService
(
self
)
self
.
root_path
=
BASE_DIR
self
.
httpd
=
None
@property
self
.
sshd
=
None
def
sshd
(
self
):
self
.
running
=
True
return
SSHServer
(
self
)
@property
def
httpd
(
self
):
return
HttpServer
(
self
)
def
make_logger
(
self
):
def
make_logger
(
self
):
create_logger
(
self
)
create_logger
(
self
)
# Todo: load some config from server like replay and common upload
def
load_extra_conf_from_server
(
self
):
pass
def
bootstrap
(
self
):
def
bootstrap
(
self
):
self
.
make_logger
()
self
.
make_logger
()
self
.
sshd
=
SSHServer
(
self
)
self
.
service
.
initial
()
self
.
httpd
=
HttpServer
(
self
)
self
.
load_extra_conf_from_server
()
self
.
initial_service
()
self
.
heartbeat
()
self
.
heartbeat
()
self
.
monitor_sessions
()
self
.
monitor_sessions
()
...
@@ -85,17 +90,20 @@ class Coco:
...
@@ -85,17 +90,20 @@ class Coco:
thread
.
start
()
thread
.
start
()
def
monitor_sessions
(
self
):
def
monitor_sessions
(
self
):
interval
=
self
.
config
[
"HEARTBEAT_INTERVAL"
]
def
func
():
def
func
():
while
not
self
.
stop_evt
.
is_set
():
while
not
self
.
stop_evt
.
is_set
():
for
s
in
self
.
sessions
:
for
s
in
self
.
sessions
:
if
s
.
stop_evt
.
is_set
():
if
not
s
.
is_finished
:
if
s
.
date_finished
is
None
:
continue
self
.
sessions
.
remove
(
s
)
if
s
.
date_finished
is
None
:
continue
self
.
remove_session
(
s
)
delta
=
datetime
.
datetime
.
now
()
-
s
.
date_finished
continue
if
delta
>
datetime
.
timedelta
(
minutes
=
1
):
delta
=
datetime
.
datetime
.
now
()
-
s
.
date_finished
self
.
sessions
.
remove
(
s
)
if
delta
>
datetime
.
timedelta
(
seconds
=
interval
*
5
):
time
.
sleep
(
self
.
config
[
"HEARTBEAT_INTERVAL"
])
self
.
remove_session
(
s
)
time
.
sleep
(
interval
)
thread
=
threading
.
Thread
(
target
=
func
)
thread
=
threading
.
Thread
(
target
=
func
)
thread
.
start
()
thread
.
start
()
...
@@ -106,7 +114,7 @@ class Coco:
...
@@ -106,7 +114,7 @@ class Coco:
def
run_forever
(
self
):
def
run_forever
(
self
):
self
.
bootstrap
()
self
.
bootstrap
()
print
(
time
.
ctime
())
print
(
time
.
ctime
())
print
(
'Coco version
%
s, more see https://www.jumpserver.org'
%
__version__
)
print
(
'Coco version
{}, more see https://www.jumpserver.org'
.
format
(
__version__
)
)
print
(
'Quit the server with CONTROL-C.'
)
print
(
'Quit the server with CONTROL-C.'
)
try
:
try
:
...
@@ -114,7 +122,7 @@ class Coco:
...
@@ -114,7 +122,7 @@ class Coco:
self
.
run_sshd
()
self
.
run_sshd
()
if
self
.
config
[
'WS_PORT'
]
!=
0
:
if
self
.
config
[
'WS_PORT'
]
!=
0
:
self
.
run_
ws
()
self
.
run_
httpd
()
self
.
stop_evt
.
wait
()
self
.
stop_evt
.
wait
()
except
KeyboardInterrupt
:
except
KeyboardInterrupt
:
...
@@ -126,7 +134,7 @@ class Coco:
...
@@ -126,7 +134,7 @@ class Coco:
thread
.
daemon
=
True
thread
.
daemon
=
True
thread
.
start
()
thread
.
start
()
def
run_
ws
(
self
):
def
run_
httpd
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
httpd
.
run
,
args
=
())
thread
=
threading
.
Thread
(
target
=
self
.
httpd
.
run
,
args
=
())
thread
.
daemon
=
True
thread
.
daemon
=
True
thread
.
start
()
thread
.
start
()
...
@@ -136,6 +144,7 @@ class Coco:
...
@@ -136,6 +144,7 @@ class Coco:
self
.
remove_client
(
client
)
self
.
remove_client
(
client
)
time
.
sleep
(
1
)
time
.
sleep
(
1
)
self
.
sshd
.
shutdown
()
self
.
sshd
.
shutdown
()
self
.
httpd
.
shutdown
()
logger
.
info
(
"Grace shutdown the server"
)
logger
.
info
(
"Grace shutdown the server"
)
def
add_client
(
self
,
client
):
def
add_client
(
self
,
client
):
...
@@ -148,12 +157,17 @@ class Coco:
...
@@ -148,12 +157,17 @@ class Coco:
try
:
try
:
self
.
clients
.
remove
(
client
)
self
.
clients
.
remove
(
client
)
logger
.
info
(
"Client
%
s leave, total
%
d now"
%
(
client
,
len
(
self
.
clients
)))
logger
.
info
(
"Client
%
s leave, total
%
d now"
%
(
client
,
len
(
self
.
clients
)))
client
.
send
(
"Closed by server"
)
client
.
send
(
"Closed by server"
)
client
.
close
()
client
.
close
()
except
:
except
:
pass
pass
def
initial_service
(
self
):
def
add_session
(
self
,
session
):
self
.
service
=
AppService
(
self
)
with
self
.
lock
:
self
.
service
.
initial
()
self
.
sessions
.
append
(
session
)
def
remove_session
(
self
,
session
):
with
self
.
lock
:
self
.
sessions
.
remove
(
session
)
coco/forward.py
View file @
e9a6daa7
...
@@ -3,9 +3,9 @@
...
@@ -3,9 +3,9 @@
import
socket
import
socket
import
threading
import
threading
import
logging
import
logging
import
time
import
paramiko
import
paramiko
import
time
from
.session
import
Session
from
.session
import
Session
from
.models
import
Server
from
.models
import
Server
...
@@ -29,10 +29,10 @@ class ProxyServer:
...
@@ -29,10 +29,10 @@ class ProxyServer:
self
.
server
=
self
.
get_server_conn
(
asset
,
system_user
)
self
.
server
=
self
.
get_server_conn
(
asset
,
system_user
)
if
self
.
server
is
None
:
if
self
.
server
is
None
:
return
return
session
=
Session
(
self
.
client
,
self
.
server
,
self
.
app
.
config
[
"SESSION_DIR"
]
)
session
=
Session
(
self
.
client
,
self
.
server
)
self
.
app
.
sessions
.
append
(
session
)
self
.
app
.
add_session
(
session
)
self
.
watch_win_size_change_async
()
self
.
watch_win_size_change_async
()
session
.
record_async
()
session
.
add_recorder
()
session
.
bridge
()
session
.
bridge
()
session
.
stop_evt
.
set
()
session
.
stop_evt
.
set
()
...
@@ -59,11 +59,22 @@ class ProxyServer:
...
@@ -59,11 +59,22 @@ class ProxyServer:
if
not
self
.
validate_permission
(
asset
,
system_user
):
if
not
self
.
validate_permission
(
asset
,
system_user
):
self
.
client
.
send
(
_
(
'No permission'
))
self
.
client
.
send
(
_
(
'No permission'
))
return
None
return
None
self
.
get_system_user_auth
(
system_user
)
self
.
get_system_user_auth
(
system_user
)
if
True
:
server
=
self
.
get_ssh_server_conn
(
asset
,
system_user
)
else
:
server
=
self
.
get_ssh_server_conn
(
asset
,
system_user
)
return
server
# Todo: Support telnet
def
get_telnet_server_conn
(
self
,
asset
,
system_user
):
pass
def
get_ssh_server_conn
(
self
,
asset
,
system_user
):
ssh
=
paramiko
.
SSHClient
()
ssh
=
paramiko
.
SSHClient
()
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
print
(
asset
.
ip
,
asset
.
port
,
system_user
.
username
,
system_user
.
password
,
system_user
.
private_key
)
print
(
asset
.
ip
,
asset
.
port
,
system_user
.
username
,
system_user
.
password
,
system_user
.
private_key
)
try
:
try
:
ssh
.
connect
(
asset
.
ip
,
port
=
asset
.
port
,
ssh
.
connect
(
asset
.
ip
,
port
=
asset
.
port
,
username
=
system_user
.
username
,
username
=
system_user
.
username
,
...
@@ -110,3 +121,4 @@ class ProxyServer:
...
@@ -110,3 +121,4 @@ class ProxyServer:
thread
=
threading
.
Thread
(
target
=
func
)
thread
=
threading
.
Thread
(
target
=
func
)
thread
.
start
()
thread
.
start
()
coco/httpd.py
View file @
e9a6daa7
...
@@ -104,3 +104,6 @@ class HttpServer:
...
@@ -104,3 +104,6 @@ class HttpServer:
ws
=
tornado
.
web
.
Application
(
self
.
routers
,
**
self
.
settings
)
ws
=
tornado
.
web
.
Application
(
self
.
routers
,
**
self
.
settings
)
ws
.
listen
(
port
=
port
,
address
=
host
)
ws
.
listen
(
port
=
port
,
address
=
host
)
tornado
.
ioloop
.
IOLoop
.
current
()
.
start
()
tornado
.
ioloop
.
IOLoop
.
current
()
.
start
()
def
shutdown
(
self
):
pass
coco/interactive.py
View file @
e9a6daa7
...
@@ -4,7 +4,7 @@ import socket
...
@@ -4,7 +4,7 @@ import socket
import
threading
import
threading
# Todo remove
# Todo remove
from
jms.models
import
Asset
,
SystemUser
,
AssetGroup
from
jms.models
import
Asset
,
AssetGroup
from
.
import
char
from
.
import
char
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_title
as
title
,
\
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_title
as
title
,
\
...
@@ -48,7 +48,7 @@ class InteractiveServer:
...
@@ -48,7 +48,7 @@ class InteractiveServer:
)
)
self
.
client
.
send
(
banner
)
self
.
client
.
send
(
banner
)
def
get_
choice
(
self
,
prompt
=
'Opt> '
):
def
get_
option
(
self
,
prompt
=
'Opt> '
):
"""实现了一个ssh input, 提示用户输入, 获取并返回
"""实现了一个ssh input, 提示用户输入, 获取并返回
:return user input string
:return user input string
...
@@ -218,7 +218,7 @@ class InteractiveServer:
...
@@ -218,7 +218,7 @@ class InteractiveServer:
while
True
:
while
True
:
self
.
client
.
send
(
wr
(
_
(
"Choose one to login: "
),
after
=
1
))
self
.
client
.
send
(
wr
(
_
(
"Choose one to login: "
),
after
=
1
))
self
.
display_system_users
(
system_users
)
self
.
display_system_users
(
system_users
)
opt
=
self
.
get_
choice
(
"ID> "
)
opt
=
self
.
get_
option
(
"ID> "
)
if
opt
.
isdigit
()
and
len
(
system_users
)
>
int
(
opt
):
if
opt
.
isdigit
()
and
len
(
system_users
)
>
int
(
opt
):
return
system_users
[
int
(
opt
)]
return
system_users
[
int
(
opt
)]
elif
opt
in
[
'q'
,
'Q'
]:
elif
opt
in
[
'q'
,
'Q'
]:
...
@@ -247,15 +247,13 @@ class InteractiveServer:
...
@@ -247,15 +247,13 @@ class InteractiveServer:
forwarder
.
proxy
(
asset
,
system_user
)
forwarder
.
proxy
(
asset
,
system_user
)
def
replay_session
(
self
,
session_id
):
def
replay_session
(
self
,
session_id
):
session
=
Session
(
self
.
client
,
None
)
pass
session
.
id
=
"5a5dbfbe-093f-4bc1-810f-e8401b9e6045"
session
.
replay
()
def
activate
(
self
):
def
interact
(
self
):
self
.
display_banner
()
self
.
display_banner
()
while
True
:
while
True
:
try
:
try
:
opt
=
self
.
get_
choice
()
opt
=
self
.
get_
option
()
self
.
dispatch
(
opt
)
self
.
dispatch
(
opt
)
except
socket
.
error
as
e
:
except
socket
.
error
as
e
:
logger
.
error
(
"Socket error
%
s"
%
e
)
logger
.
error
(
"Socket error
%
s"
%
e
)
...
@@ -263,7 +261,7 @@ class InteractiveServer:
...
@@ -263,7 +261,7 @@ class InteractiveServer:
self
.
close
()
self
.
close
()
def
activate_async
(
self
):
def
activate_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
activate
)
thread
=
threading
.
Thread
(
target
=
self
.
interact
)
thread
.
daemon
=
True
thread
.
daemon
=
True
thread
.
start
()
thread
.
start
()
...
...
coco/models.py
View file @
e9a6daa7
import
json
import
json
import
queue
import
threading
import
threading
import
datetime
import
datetime
...
@@ -57,9 +58,8 @@ class Server:
...
@@ -57,9 +58,8 @@ class Server:
Server object like client, a wrapper object, a connection to the asset,
Server object like client, a wrapper object, a connection to the asset,
Because we don't want to using python dynamic feature, such asset
Because we don't want to using python dynamic feature, such asset
have the chan and system_user attr.
have the chan and system_user attr.
"""
"""
# Todo: Server name is not very
proper
# Todo: Server name is not very
suitable
def
__init__
(
self
,
chan
,
asset
,
system_user
):
def
__init__
(
self
,
chan
,
asset
,
system_user
):
self
.
chan
=
chan
self
.
chan
=
chan
self
.
asset
=
asset
self
.
asset
=
asset
...
@@ -69,12 +69,27 @@ class Server:
...
@@ -69,12 +69,27 @@ class Server:
self
.
input_data
=
[]
self
.
input_data
=
[]
self
.
output_data
=
[]
self
.
output_data
=
[]
self
.
input
=
''
self
.
output
=
''
self
.
_in_input_state
=
True
self
.
_in_input_state
=
True
self
.
_input_initial
=
False
self
.
_input_initial
=
False
self
.
_in_vim_state
=
False
self
.
_in_vim_state
=
False
self
.
recorders
=
[]
self
.
queue
=
queue
.
Queue
()
def
add_recorder
(
self
,
recorder
):
self
.
recorders
.
append
(
recorder
)
def
remove_recorder
(
self
,
recorder
):
self
.
recorders
.
remove
(
recorder
)
def
record_command
(
self
,
_input
,
_output
):
while
True
:
_input
,
_output
=
self
.
queue
.
get
()
for
recorder
in
self
.
recorders
:
t
=
threading
.
Thread
(
target
=
recorder
.
record_command
,
args
=
(
_input
,
_output
))
t
.
start
()
def
fileno
(
self
):
def
fileno
(
self
):
return
self
.
chan
.
fileno
()
return
self
.
chan
.
fileno
()
...
@@ -89,24 +104,21 @@ class Server:
...
@@ -89,24 +104,21 @@ class Server:
else
:
else
:
if
not
self
.
_in_input_state
:
if
not
self
.
_in_input_state
:
print
(
"#"
*
30
+
" 新周期 "
+
"#"
*
30
)
print
(
"#"
*
30
+
" 新周期 "
+
"#"
*
30
)
self
.
_parse_input
()
_input
=
self
.
_parse_input
()
self
.
_parse_output
()
_output
=
self
.
_parse_output
()
self
.
record_command
(
_input
,
_output
)
del
self
.
input_data
[:]
del
self
.
input_data
[:]
del
self
.
output_data
[:]
del
self
.
output_data
[:]
self
.
_in_input_state
=
True
self
.
_in_input_state
=
True
print
(
"Send:
%
s"
%
b
)
return
self
.
chan
.
send
(
b
)
return
self
.
chan
.
send
(
b
)
def
recv
(
self
,
size
):
def
recv
(
self
,
size
):
data
=
self
.
chan
.
recv
(
size
)
data
=
self
.
chan
.
recv
(
size
)
print
(
"Recv:
%
s"
%
data
)
if
self
.
_input_initial
:
if
self
.
_input_initial
:
if
self
.
_in_input_state
:
if
self
.
_in_input_state
:
self
.
input_data
.
append
(
data
)
self
.
input_data
.
append
(
data
)
else
:
else
:
self
.
output_data
.
append
(
data
)
self
.
output_data
.
append
(
data
)
return
data
return
data
def
close
(
self
):
def
close
(
self
):
...
@@ -122,18 +134,19 @@ class Server:
...
@@ -122,18 +134,19 @@ class Server:
def
_parse_output
(
self
):
def
_parse_output
(
self
):
parser
=
utils
.
TtyIOParser
()
parser
=
utils
.
TtyIOParser
()
print
(
"
\t
Output:
%
s"
%
parser
.
parse_output
(
self
.
output_data
)
)
return
parser
.
parse_output
(
self
.
output_data
)
def
_parse_input
(
self
):
def
_parse_input
(
self
):
parser
=
utils
.
TtyIOParser
()
parser
=
utils
.
TtyIOParser
()
print
(
"
\t
Input:
%
s"
%
parser
.
parse_input
(
self
.
input_data
)
)
return
parser
.
parse_input
(
self
.
input_data
)
def
__getattr__
(
self
,
item
):
def
__getattr__
(
self
,
item
):
return
getattr
(
self
.
chan
,
item
)
return
getattr
(
self
.
chan
,
item
)
def
__str__
(
self
):
def
__str__
(
self
):
return
"<
%
s@
%
s:
%
s>"
%
(
self
.
system_user
.
username
,
return
"<
%
s@
%
s:
%
s>"
%
(
self
.
system_user
.
username
,
self
.
asset
.
hostname
,
self
.
asset
.
port
)
self
.
asset
.
hostname
,
self
.
asset
.
port
)
class
WSProxy
:
class
WSProxy
:
...
...
coco/record.py
View file @
e9a6daa7
...
@@ -2,8 +2,60 @@
...
@@ -2,8 +2,60 @@
#
#
import
abc
import
abc
import
datetime
import
multiprocessing
import
threading
import
time
import
time
import
datetime
import
socket
import
os
import
logging
logger
=
logging
.
getLogger
(
__file__
)
BUF_SIZE
=
1024
class
Recorder
(
metaclass
=
abc
.
ABCMeta
):
def
__init__
(
self
,
app
,
session
):
self
.
app
=
app
self
.
session
=
session
self
.
replay_queue
=
multiprocessing
.
Queue
()
self
.
command_queue
=
multiprocessing
.
Queue
()
self
.
stop_evt
=
multiprocessing
.
Event
()
@abc.abstractmethod
def
record_replay
(
self
):
pass
@abc.abstractmethod
def
record_command
(
self
,
_input
,
_output
):
pass
class
FileRecorder
(
Recorder
):
def
record_replay
(
self
):
parent
,
child
=
socket
.
socketpair
()
self
.
session
.
add_watcher
(
parent
)
session_dir
=
self
.
app
.
config
[
"SESSION_DIR"
]
with
open
(
os
.
path
.
join
(
session_dir
,
session
.
id
+
".rec"
),
'wb'
)
as
dataf
,
\
open
(
os
.
path
.
join
(
session_dir
,
session
.
id
+
".time"
),
"w"
)
as
timef
:
dataf
.
write
(
"Script started on {}
\n
"
.
format
(
time
.
asctime
())
.
encode
(
"utf-8"
))
while
not
self
.
stop_evt
.
is_set
():
start_t
=
time
.
time
()
data
=
child
.
recv
(
BUF_SIZE
)
end_t
=
time
.
time
()
size
=
len
(
data
)
if
size
==
0
:
break
timef
.
write
(
"
%.4
f
%
s
\n
"
%
(
end_t
-
start_t
,
size
))
dataf
.
write
(
data
)
dataf
.
write
(
"Script done on {}
\n
"
.
format
(
time
.
asctime
())
.
encode
(
"utf-8"
))
def
record_command
(
self
,
_input
,
_output
):
pass
class
SessionReplay
(
metaclass
=
abc
.
ABCMeta
):
class
SessionReplay
(
metaclass
=
abc
.
ABCMeta
):
...
@@ -17,7 +69,7 @@ class SessionReplay(metaclass=abc.ABCMeta):
...
@@ -17,7 +69,7 @@ class SessionReplay(metaclass=abc.ABCMeta):
pass
pass
@abc.abstractmethod
@abc.abstractmethod
def
replay
(
self
,
sock
):
def
done
(
self
):
pass
pass
...
@@ -52,6 +104,9 @@ class FileSessionReplay(SessionReplay):
...
@@ -52,6 +104,9 @@ class FileSessionReplay(SessionReplay):
sock
.
send
(
data
)
sock
.
send
(
data
)
sock
.
send
(
"Replay session end"
)
sock
.
send
(
"Replay session end"
)
def
done
(
self
):
pass
class
FileSessionCommand
(
SessionCommand
):
class
FileSessionCommand
(
SessionCommand
):
...
@@ -63,3 +118,6 @@ class FileSessionCommand(SessionCommand):
...
@@ -63,3 +118,6 @@ class FileSessionCommand(SessionCommand):
self
.
f
.
write
(
"$ {}
\n
"
.
format
(
cmd
))
self
.
f
.
write
(
"$ {}
\n
"
.
format
(
cmd
))
self
.
f
.
write
(
"{}
\n\n
"
.
format
(
output
))
self
.
f
.
write
(
"{}
\n\n
"
.
format
(
output
))
def
done
(
self
):
pass
coco/session.py
View file @
e9a6daa7
...
@@ -16,17 +16,17 @@ logger = logging.getLogger(__file__)
...
@@ -16,17 +16,17 @@ logger = logging.getLogger(__file__)
class
Session
:
class
Session
:
def
__init__
(
self
,
client
,
server
,
session_dir
):
def
__init__
(
self
,
client
,
server
):
self
.
id
=
str
(
uuid
.
uuid4
())
self
.
id
=
str
(
uuid
.
uuid4
())
self
.
client
=
client
# Master of the session, it's a client sock
self
.
client
=
client
# Master of the session, it's a client sock
self
.
server
=
server
# Server channel
self
.
server
=
server
# Server channel
self
.
session_dir
=
session_dir
# Dir to save session record
self
.
watchers
=
[]
# Only watch session
self
.
watchers
=
[]
# Only watch session
self
.
sharers
=
[]
# Join to the session, read and write
self
.
sharers
=
[]
# Join to the session, read and write
self
.
stop_evt
=
threading
.
Event
()
self
.
replaying
=
True
self
.
replaying
=
True
self
.
date_created
=
datetime
.
datetime
.
now
()
self
.
date_created
=
datetime
.
datetime
.
now
()
self
.
date_finished
=
None
self
.
date_finished
=
None
self
.
recorder
=
[]
self
.
stop_evt
=
threading
.
Event
()
self
.
sel
=
selectors
.
DefaultSelector
()
self
.
sel
=
selectors
.
DefaultSelector
()
def
add_watcher
(
self
,
watcher
,
silent
=
False
):
def
add_watcher
(
self
,
watcher
,
silent
=
False
):
...
@@ -65,7 +65,8 @@ class Session:
...
@@ -65,7 +65,8 @@ class Session:
def
remove_sharer
(
self
,
sharer
):
def
remove_sharer
(
self
,
sharer
):
logger
.
info
(
"Session
%
s remove sharer
%
s"
%
(
self
.
id
,
sharer
))
logger
.
info
(
"Session
%
s remove sharer
%
s"
%
(
self
.
id
,
sharer
))
sharer
.
send
(
"Leave session {} at {}"
sharer
.
send
(
"Leave session {} at {}"
.
format
(
self
.
id
,
datetime
.
datetime
.
now
())
.
encode
(
"utf-8"
))
.
format
(
self
.
id
,
datetime
.
datetime
.
now
())
.
encode
(
"utf-8"
))
self
.
sel
.
unregister
(
sharer
)
self
.
sel
.
unregister
(
sharer
)
self
.
sharers
.
remove
(
sharer
)
self
.
sharers
.
remove
(
sharer
)
...
@@ -91,78 +92,39 @@ class Session:
...
@@ -91,78 +92,39 @@ class Session:
elif
sock
==
self
.
client
:
elif
sock
==
self
.
client
:
if
len
(
data
)
==
0
:
if
len
(
data
)
==
0
:
for
watcher
in
self
.
watchers
+
self
.
sharers
:
for
watcher
in
self
.
watchers
+
self
.
sharers
:
watcher
.
send
(
"Client {} close the session"
.
format
(
self
.
client
)
.
encode
(
"utf-8"
))
watcher
.
send
(
"Client {} close the session"
.
format
(
self
.
client
)
.
encode
(
"utf-8"
))
self
.
close
()
self
.
close
()
break
break
self
.
server
.
send
(
data
)
self
.
server
.
send
(
data
)
elif
sock
in
self
.
sharers
:
elif
sock
in
self
.
sharers
:
if
len
(
data
)
==
0
:
if
len
(
data
)
==
0
:
logger
.
info
(
"Sharer
%
s leave session
%
s"
%
(
sock
,
self
.
id
))
logger
.
info
(
"Sharer {} leave session {}"
.
format
(
sock
,
self
.
id
))
self
.
remove_sharer
(
sock
)
self
.
remove_sharer
(
sock
)
self
.
server
.
send
(
data
)
self
.
server
.
send
(
data
)
elif
sock
in
self
.
watchers
:
elif
sock
in
self
.
watchers
:
if
len
(
data
)
==
0
:
if
len
(
data
)
==
0
:
logger
.
info
(
"Watcher
%
s leave session
%
s"
%
(
sock
,
self
.
id
))
logger
.
info
(
"Watcher {} leave session {}"
.
format
(
sock
,
self
.
id
))
def
set_size
(
self
,
width
,
height
):
def
set_size
(
self
,
width
,
height
):
self
.
server
.
resize_pty
(
width
=
width
,
height
=
height
)
self
.
server
.
resize_pty
(
width
=
width
,
height
=
height
)
def
add_recorder
(
self
,
recorder
):
self
.
recorder
.
append
(
recorder
)
def
remove_recorder
(
self
,
recorder
):
self
.
recorder
.
remove
(
recorder
)
def
record
(
self
):
def
record
(
self
):
"""
"""
Record the session to a file. Using it replay in the future
Record the session
:return:
"""
logger
.
info
(
"Start record session
%
s"
%
self
.
id
)
parent
,
child
=
socket
.
socketpair
()
self
.
add_watcher
(
parent
)
record_dir
=
os
.
path
.
join
(
self
.
session_dir
,
self
.
date_created
.
strftime
(
"
%
Y-
%
m-
%
d"
))
if
not
os
.
path
.
isdir
(
record_dir
):
os
.
mkdir
(
record_dir
)
with
open
(
os
.
path
.
join
(
record_dir
,
self
.
id
+
".rec"
),
'wb'
)
as
dataf
,
\
open
(
os
.
path
.
join
(
record_dir
,
self
.
id
+
".time"
),
"w"
)
as
timef
:
dataf
.
write
(
"Script started on {}
\n
"
.
format
(
time
.
asctime
())
.
encode
(
"utf-8"
))
while
not
self
.
stop_evt
.
is_set
():
start_t
=
time
.
time
()
data
=
child
.
recv
(
BUF_SIZE
)
end_t
=
time
.
time
()
size
=
len
(
data
)
if
size
==
0
:
break
timef
.
write
(
"
%.4
f
%
s
\n
"
%
(
end_t
-
start_t
,
size
))
dataf
.
write
(
data
)
dataf
.
write
(
"Script done on {}
\n
"
.
format
(
time
.
asctime
())
.
encode
(
"utf-8"
))
logger
.
info
(
"End session record
%
s"
%
self
.
id
)
def
record_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
record
)
thread
.
start
()
def
replay
(
self
):
"""
Replay the session
:return:
"""
with
open
(
os
.
path
.
join
(
self
.
session_dir
,
self
.
id
+
".rec"
),
'rb'
)
as
dataf
,
\
open
(
os
.
path
.
join
(
self
.
session_dir
,
self
.
id
+
".time"
),
"r"
)
as
timef
:
self
.
client
.
send
(
dataf
.
readline
())
for
l
in
timef
:
if
not
self
.
replaying
:
break
t
,
size
=
float
(
l
.
split
()[
0
]),
int
(
l
.
split
()[
1
])
data
=
dataf
.
read
(
size
)
time
.
sleep
(
t
)
self
.
client
.
send
(
data
)
self
.
client
.
send
(
"Replay session {} end"
.
format
(
self
.
id
)
.
encode
(
'utf-8'
))
self
.
replaying
=
False
def
replay_download
(
self
):
"""
Using termrecord generate html, then down user download it and share it
:return:
:return:
"""
"""
pass
for
recorder
in
self
.
recorder
:
recorder
.
record
(
self
)
def
close
(
self
):
def
close
(
self
):
self
.
stop_evt
.
set
()
self
.
stop_evt
.
set
()
...
...
coco/sshd.py
View file @
e9a6daa7
...
@@ -17,9 +17,9 @@ BACKLOG = 5
...
@@ -17,9 +17,9 @@ BACKLOG = 5
class
SSHServer
:
class
SSHServer
:
def
__init__
(
self
,
app
=
None
):
def
__init__
(
self
,
app
):
self
.
app
=
app
self
.
app
=
app
self
.
stop_ev
en
t
=
threading
.
Event
()
self
.
stop_evt
=
threading
.
Event
()
self
.
sock
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
self
.
sock
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
self
.
host_key_path
=
os
.
path
.
join
(
self
.
app
.
root_path
,
'keys'
,
'host_rsa_key'
)
self
.
host_key_path
=
os
.
path
.
join
(
self
.
app
.
root_path
,
'keys'
,
'host_rsa_key'
)
...
@@ -37,12 +37,11 @@ class SSHServer:
...
@@ -37,12 +37,11 @@ class SSHServer:
def
run
(
self
):
def
run
(
self
):
host
=
self
.
app
.
config
[
"BIND_HOST"
]
host
=
self
.
app
.
config
[
"BIND_HOST"
]
port
=
self
.
app
.
config
[
"SSHD_PORT"
]
port
=
self
.
app
.
config
[
"SSHD_PORT"
]
print
(
'Starting ssh server at
%(host)
s:
%(port)
s'
%
print
(
'Starting ssh server at {}:{}'
.
format
(
host
,
port
))
{
"host"
:
host
,
"port"
:
port
})
self
.
sock
.
setsockopt
(
socket
.
SOL_SOCKET
,
socket
.
SO_REUSEADDR
,
1
)
self
.
sock
.
setsockopt
(
socket
.
SOL_SOCKET
,
socket
.
SO_REUSEADDR
,
1
)
self
.
sock
.
bind
((
host
,
port
))
self
.
sock
.
bind
((
host
,
port
))
self
.
sock
.
listen
(
BACKLOG
)
self
.
sock
.
listen
(
BACKLOG
)
while
not
self
.
stop_ev
en
t
.
is_set
():
while
not
self
.
stop_evt
.
is_set
():
try
:
try
:
sock
,
addr
=
self
.
sock
.
accept
()
sock
,
addr
=
self
.
sock
.
accept
()
logger
.
info
(
"Get ssh request from
%
s:
%
s"
%
(
addr
[
0
],
addr
[
1
]))
logger
.
info
(
"Get ssh request from
%
s:
%
s"
%
(
addr
[
0
],
addr
[
1
]))
...
@@ -65,7 +64,7 @@ class SSHServer:
...
@@ -65,7 +64,7 @@ class SSHServer:
try
:
try
:
transport
.
start_server
(
server
=
server
)
transport
.
start_server
(
server
=
server
)
except
paramiko
.
SSHException
:
except
paramiko
.
SSHException
:
logger
.
warning
(
"SSH negotiation failed
.
"
)
logger
.
warning
(
"SSH negotiation failed"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
except
EOFError
:
except
EOFError
:
logger
.
warning
(
"EOF Error"
)
logger
.
warning
(
"EOF Error"
)
...
@@ -88,7 +87,7 @@ class SSHServer:
...
@@ -88,7 +87,7 @@ class SSHServer:
def
dispatch
(
self
,
client
):
def
dispatch
(
self
,
client
):
request_type
=
client
.
request
.
type
request_type
=
client
.
request
.
type
if
request_type
==
'pty'
:
if
request_type
==
'pty'
:
InteractiveServer
(
self
.
app
,
client
)
.
activate
()
InteractiveServer
(
self
.
app
,
client
)
.
interact
()
elif
request_type
==
'exec'
:
elif
request_type
==
'exec'
:
pass
pass
elif
request_type
==
'subsystem'
:
elif
request_type
==
'subsystem'
:
...
@@ -97,4 +96,4 @@ class SSHServer:
...
@@ -97,4 +96,4 @@ class SSHServer:
client
.
send
(
"Not support request type:
%
s"
%
request_type
)
client
.
send
(
"Not support request type:
%
s"
%
request_type
)
def
shutdown
(
self
):
def
shutdown
(
self
):
self
.
stop_ev
en
t
.
set
()
self
.
stop_evt
.
set
()
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