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
08a5c616
Commit
08a5c616
authored
7 years ago
by
广宏伟
Browse files
Options
Browse Files
Download
Plain Diff
Merged in test (pull request #11)
Test
parents
e18f5feb
368d12b7
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
172 additions
and
90 deletions
+172
-90
app.py
coco/app.py
+24
-16
httpd.py
coco/httpd.py
+2
-2
interactive.py
coco/interactive.py
+5
-6
interface.py
coco/interface.py
+4
-4
logger.py
coco/logger.py
+44
-31
models.py
coco/models.py
+1
-2
proxy.py
coco/proxy.py
+2
-3
recorder.py
coco/recorder.py
+63
-8
session.py
coco/session.py
+3
-2
sshd.py
coco/sshd.py
+3
-9
tasks.py
coco/tasks.py
+3
-2
utils.py
coco/utils.py
+5
-0
conf_docker.py
conf_docker.py
+4
-0
conf_example.py
conf_example.py
+7
-4
requirements.txt
requirements/requirements.txt
+2
-1
No files found.
coco/app.py
View file @
08a5c616
...
...
@@ -6,8 +6,8 @@ import datetime
import
os
import
time
import
threading
import
logging
import
socket
import
json
from
jms.service
import
AppService
...
...
@@ -17,12 +17,13 @@ from .httpd import HttpServer
from
.logger
import
create_logger
from
.tasks
import
TaskHandler
from
.recorder
import
get_command_recorder_class
,
get_replay_recorder_class
from
.utils
import
get_logger
__version__
=
'0.
4
.0'
__version__
=
'0.
5
.0'
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
))
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
class
Coco
:
...
...
@@ -42,19 +43,18 @@ class Coco:
'LOG_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'logs'
),
'SESSION_DIR'
:
os
.
path
.
join
(
BASE_DIR
,
'sessions'
),
'ASSET_LIST_SORT_BY'
:
'hostname'
,
# hostname, ip
'
SSH_
PASSWORD_AUTH'
:
True
,
'
SSH_
PUBLIC_KEY_AUTH'
:
True
,
'PASSWORD_AUTH'
:
True
,
'PUBLIC_KEY_AUTH'
:
True
,
'HEARTBEAT_INTERVAL'
:
5
,
'MAX_CONNECTIONS'
:
500
,
'ADMINS'
:
''
,
'
REPLAY_RECORD_ENGINE'
:
'server'
,
# local,
server
'
COMMAND_RECORD_ENGINE'
:
'server'
,
# local, server, elasticsearch(not yet)
'
COMMAND_STORAGE'
:
{
'TYPE'
:
'server'
},
#
server
'
REPLAY_RECORD_ENGINE'
:
'server'
,
}
def
__init__
(
self
,
name
=
None
,
root_path
=
None
):
def
__init__
(
self
,
root_path
=
None
):
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
.
clients
=
[]
self
.
lock
=
threading
.
Lock
()
...
...
@@ -66,6 +66,10 @@ class Coco:
self
.
command_recorder_class
=
None
self
.
_task_handler
=
None
@property
def
name
(
self
):
return
self
.
config
[
"NAME"
]
@property
def
service
(
self
):
if
self
.
_service
is
None
:
...
...
@@ -93,16 +97,20 @@ class Coco:
def
make_logger
(
self
):
create_logger
(
self
)
# Todo: load some config from server like replay and common upload
def
load_extra_conf_from_server
(
self
):
pass
configs
=
self
.
service
.
load_config_from_server
()
logger
.
debug
(
"Loading config from server: {}"
.
format
(
json
.
dumps
(
configs
)
))
self
.
config
.
update
(
configs
)
def
initial_recorder
(
self
):
self
.
replay_recorder_class
=
get_replay_recorder_class
(
self
)
self
.
command_recorder_class
=
get_command_recorder_class
(
self
)
def
get_recorder_class
(
self
):
self
.
replay_recorder_class
=
get_replay_recorder_class
(
self
.
config
)
self
.
command_recorder_class
=
get_command_recorder_class
(
self
.
config
)
def
new_command_recorder
(
self
):
return
self
.
command_recorder_class
(
self
)
recorder
=
self
.
command_recorder_class
(
self
)
return
recorder
def
new_replay_recorder
(
self
):
return
self
.
replay_recorder_class
(
self
)
...
...
@@ -111,7 +119,7 @@ class Coco:
self
.
make_logger
()
self
.
service
.
initial
()
self
.
load_extra_conf_from_server
()
self
.
initial_recorder
()
self
.
get_recorder_class
()
self
.
keep_heartbeat
()
self
.
monitor_sessions
()
...
...
This diff is collapsed.
Click to expand it.
coco/httpd.py
View file @
08a5c616
...
...
@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
#
import
os
import
logging
import
socket
from
flask_socketio
import
SocketIO
,
Namespace
,
emit
,
join_room
,
leave_room
from
flask
import
Flask
,
send_from_directory
,
render_template
,
request
,
jsonify
...
...
@@ -12,11 +11,12 @@ import uuid
from
jms.models
import
User
from
.models
import
Request
,
Client
,
WSProxy
from
.proxy
import
ProxyServer
from
.utils
import
get_logger
__version__
=
'0.4.0'
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
))
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
class
BaseWebSocketHandler
:
...
...
This diff is collapsed.
Click to expand it.
coco/interactive.py
View file @
08a5c616
...
...
@@ -2,22 +2,21 @@
# -*- coding: utf-8 -*-
#
import
logging
import
socket
import
threading
import
weakref
import
os
from
jms.models
import
Asset
,
AssetGroup
from
.
import
char
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_title
as
title
,
\
wrap_with_primary
as
primary
,
wrap_with_warning
as
warning
,
\
is_obj_attr_has
,
is_obj_attr_eq
,
sort_assets
,
TtyIOParser
,
\
ugettext
as
_
ugettext
as
_
,
get_logger
from
.proxy
import
ProxyServer
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
class
InteractiveServer
:
...
...
@@ -42,7 +41,7 @@ class InteractiveServer:
if
self
.
_search_result
:
return
self
.
_search_result
else
:
return
None
return
[]
@search_result.setter
def
search_result
(
self
,
value
):
...
...
@@ -272,7 +271,7 @@ class InteractiveServer:
def
search_and_proxy
(
self
,
opt
):
self
.
search_assets
(
opt
)
if
len
(
self
.
search_result
)
==
1
:
if
self
.
search_result
and
len
(
self
.
search_result
)
==
1
:
self
.
proxy
(
self
.
search_result
[
0
])
else
:
self
.
display_search_result
()
...
...
This diff is collapsed.
Click to expand it.
coco/interface.py
View file @
08a5c616
...
...
@@ -2,13 +2,13 @@
# -*- coding: utf-8 -*-
#
import
logging
import
paramiko
import
threading
import
weakref
from
.utils
import
get_logger
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
class
SSHInterface
(
paramiko
.
ServerInterface
):
...
...
@@ -43,9 +43,9 @@ class SSHInterface(paramiko.ServerInterface):
def
get_allowed_auths
(
self
,
username
):
supported
=
[]
if
self
.
app
.
config
[
"
SSH_
PASSWORD_AUTH"
]:
if
self
.
app
.
config
[
"PASSWORD_AUTH"
]:
supported
.
append
(
"password"
)
if
self
.
app
.
config
[
"
SSH_
PUBLIC_KEY_AUTH"
]:
if
self
.
app
.
config
[
"PUBLIC_KEY_AUTH"
]:
supported
.
append
(
"publickey"
)
return
","
.
join
(
supported
)
...
...
This diff is collapsed.
Click to expand it.
coco/logger.py
View file @
08a5c616
...
...
@@ -4,43 +4,56 @@
import
os
import
logging
from
logging
import
StreamHandler
from
logging.handlers
import
TimedRotatingFileHandler
LOG_LEVELS
=
{
'DEBUG'
:
logging
.
DEBUG
,
'INFO'
:
logging
.
INFO
,
'WARN'
:
logging
.
WARNING
,
'WARNING'
:
logging
.
WARNING
,
'ERROR'
:
logging
.
ERROR
,
'FATAL'
:
logging
.
FATAL
,
'CRITICAL'
:
logging
.
CRITICAL
,
}
from
logging.config
import
dictConfig
def
create_logger
(
app
):
level
=
app
.
config
[
'LOG_LEVEL'
]
level
=
LOG_LEVELS
.
get
(
level
,
logging
.
INFO
)
log_dir
=
app
.
config
.
get
(
'LOG_DIR'
)
log_path
=
os
.
path
.
join
(
log_dir
,
'coco.log'
)
main_setting
=
{
'handlers'
:
[
'console'
,
'file'
],
'level'
:
level
,
'propagate'
:
False
,
}
config
=
dict
(
version
=
1
,
formatters
=
{
"main"
:
{
'format'
:
'
%(asctime)
s [
%(module)
s
%(levelname)
s]
%(message)
s'
,
'datefmt'
:
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
,
},
'simple'
:
{
'format'
:
'
%(asctime)
s [
%(levelname)-8
s]
%(message)
s'
,
'datefmt'
:
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
,
}
},
handlers
=
{
'null'
:
{
'level'
:
'DEBUG'
,
'class'
:
'logging.NullHandler'
,
},
'console'
:
{
'level'
:
'DEBUG'
,
'class'
:
'logging.StreamHandler'
,
'formatter'
:
'main'
},
'file'
:
{
'level'
:
'DEBUG'
,
'class'
:
'logging.FileHandler'
,
'formatter'
:
'main'
,
'filename'
:
log_path
,
},
},
loggers
=
{
'coco'
:
main_setting
,
'paramiko'
:
main_setting
,
'jms'
:
main_setting
,
}
)
dictConfig
(
config
)
logger
=
logging
.
getLogger
()
return
logger
main_formatter
=
logging
.
Formatter
(
fmt
=
'
%(asctime)
s [
%(module)
s
%(levelname)
s]
%(message)
s'
,
datefmt
=
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
# main_formatter = logging.Formatter(
# fmt='%(asctime)s [%(levelname)s] %(message)s',
# datefmt='%Y-%m-%d %H:%M:%S'
# )
console_handler
=
StreamHandler
()
file_handler
=
TimedRotatingFileHandler
(
filename
=
log_path
,
when
=
'D'
,
backupCount
=
10
)
for
handler
in
[
console_handler
,
file_handler
]:
handler
.
setFormatter
(
main_formatter
)
logger
.
addHandler
(
handler
)
logger
.
setLevel
(
level
)
logging
.
getLogger
(
"requests"
)
.
setLevel
(
logging
.
WARNING
)
This diff is collapsed.
Click to expand it.
coco/models.py
View file @
08a5c616
...
...
@@ -2,14 +2,13 @@
# -*- coding: utf-8 -*-
import
threading
import
datetime
import
logging
import
weakref
from
.
import
char
from
.
import
utils
BUF_SIZE
=
4096
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
utils
.
get_l
ogger
(
__file__
)
class
Request
:
...
...
This diff is collapsed.
Click to expand it.
coco/proxy.py
View file @
08a5c616
...
...
@@ -4,7 +4,6 @@
import
socket
import
threading
import
logging
import
time
import
weakref
import
paramiko
...
...
@@ -13,10 +12,10 @@ from paramiko.ssh_exception import SSHException
from
.session
import
Session
from
.models
import
Server
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_warning
as
warning
,
\
get_private_key_fingerprint
get_private_key_fingerprint
,
get_logger
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
TIMEOUT
=
8
BUF_SIZE
=
4096
...
...
This diff is collapsed.
Click to expand it.
coco/recorder.py
View file @
08a5c616
...
...
@@ -3,7 +3,6 @@
#
import
abc
import
logging
import
threading
import
time
import
os
...
...
@@ -11,9 +10,12 @@ import gzip
import
json
import
shutil
from
jms_es_sdk
import
ESStore
from
.utils
import
get_logger
from
.alignment
import
MemoryQueue
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
BUF_SIZE
=
1024
...
...
@@ -183,17 +185,70 @@ class ServerCommandRecorder(CommandRecorder, metaclass=Singleton):
print
(
"{} has been gc"
.
format
(
self
))
def
get_command_recorder_class
(
app
):
command_engine
=
app
.
config
[
"COMMAND_RECORD_ENGINE"
]
class
ESCommandRecorder
(
CommandRecorder
,
metaclass
=
Singleton
):
batch_size
=
10
timeout
=
5
no
=
0
default_hosts
=
[
"http://localhost"
]
if
command_engine
==
"server"
:
return
ServerCommandRecorder
def
__init__
(
self
,
app
):
super
()
.
__init__
(
app
)
self
.
queue
=
MemoryQueue
()
self
.
stop_evt
=
threading
.
Event
()
self
.
push_to_es_async
()
self
.
__class__
.
no
+=
1
self
.
store
=
ESStore
(
app
.
config
[
"COMMAND_STORAGE"
]
.
get
(
"HOSTS"
,
self
.
default_hosts
))
if
not
self
.
store
.
ping
():
raise
AssertionError
(
"ESCommand storage init error"
)
def
record
(
self
,
data
):
if
data
and
data
[
'input'
]:
data
[
'input'
]
=
data
[
'input'
][:
128
]
data
[
'output'
]
=
data
[
'output'
][:
1024
]
data
[
'timestamp'
]
=
int
(
data
[
'timestamp'
])
self
.
queue
.
put
(
data
)
def
push_to_es_async
(
self
):
def
func
():
while
not
self
.
stop_evt
.
is_set
():
data_set
=
self
.
queue
.
mget
(
self
.
batch_size
,
timeout
=
self
.
timeout
)
logger
.
debug
(
"<Session command recorder {}> queue size: {}"
.
format
(
self
.
no
,
self
.
queue
.
qsize
())
)
if
not
data_set
:
continue
logger
.
debug
(
"Send {} commands to server"
.
format
(
len
(
data_set
)))
ok
=
self
.
store
.
bulk_save
(
data_set
)
if
not
ok
:
self
.
queue
.
mput
(
data_set
)
thread
=
threading
.
Thread
(
target
=
func
)
thread
.
daemon
=
True
thread
.
start
()
def
session_start
(
self
,
session_id
):
pass
def
session_end
(
self
,
session_id
):
pass
def
__del__
(
self
):
print
(
"{} has been gc"
.
format
(
self
))
def
get_command_recorder_class
(
config
):
command_storage
=
config
[
"COMMAND_STORAGE"
]
if
command_storage
[
'TYPE'
]
==
"elasticsearch"
:
return
ESCommandRecorder
else
:
return
ServerCommandRecorder
def
get_replay_recorder_class
(
app
):
replay_engine
=
app
.
config
[
"REPLAY_RECORD_ENGINE"
]
def
get_replay_recorder_class
(
config
):
replay_engine
=
config
[
"REPLAY_RECORD_ENGINE"
]
if
replay_engine
==
"server"
:
return
ServerReplayRecorder
else
:
...
...
This diff is collapsed.
Click to expand it.
coco/session.py
View file @
08a5c616
...
...
@@ -3,13 +3,14 @@
#
import
threading
import
uuid
import
logging
import
datetime
import
selectors
import
time
from
.utils
import
get_logger
BUF_SIZE
=
1024
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
class
Session
:
...
...
This diff is collapsed.
Click to expand it.
coco/sshd.py
View file @
08a5c616
...
...
@@ -3,20 +3,16 @@
#
import
os
import
logging
import
socket
import
threading
import
paramiko
import
sys
import
time
from
.utils
import
ssh_key_gen
from
.utils
import
ssh_key_gen
,
get_logger
from
.interface
import
SSHInterface
from
.interactive
import
InteractiveServer
from
.models
import
Client
,
Request
logger
=
logging
.
getL
ogger
(
__file__
)
logger
=
get_l
ogger
(
__file__
)
BACKLOG
=
5
...
...
@@ -90,14 +86,12 @@ class SSHServer:
def
handle_chan
(
self
,
chan
,
request
):
client
=
Client
(
chan
,
request
)
print
(
chan
)
print
(
request
)
self
.
app
.
add_client
(
client
)
self
.
dispatch
(
client
)
def
dispatch
(
self
,
client
):
request_type
=
client
.
request
.
type
if
request_type
==
'pty'
:
if
request_type
==
'pty'
or
request_type
==
'x11'
:
logger
.
info
(
"Request type `pty`, dispatch to interactive mode"
)
InteractiveServer
(
self
.
app
,
client
)
.
interact
()
elif
request_type
==
'exec'
:
...
...
This diff is collapsed.
Click to expand it.
coco/tasks.py
View file @
08a5c616
...
...
@@ -2,9 +2,10 @@
# -*- coding: utf-8 -*-
#
import
weakref
import
logging
logger
=
logging
.
getLogger
(
__file__
)
from
.utils
import
get_logger
logger
=
get_logger
(
__file__
)
class
TaskHandler
:
...
...
This diff is collapsed.
Click to expand it.
coco/utils.py
View file @
08a5c616
...
...
@@ -5,6 +5,7 @@
from
__future__
import
unicode_literals
import
hashlib
import
logging
import
re
import
os
import
threading
...
...
@@ -371,4 +372,8 @@ def compile_message():
pass
def
get_logger
(
file_name
):
return
logging
.
getLogger
(
'coco.'
+
file_name
)
ugettext
=
_gettext
()
This diff is collapsed.
Click to expand it.
conf_docker.py
View file @
08a5c616
...
...
@@ -60,6 +60,10 @@ class Config:
# Admin的名字,出问题会提示给用户
ADMINS
=
os
.
environ
.
get
(
"ADMINS"
)
or
''
COMMAND_STORAGE
=
{
"TYPE"
:
"server"
}
class
ConfigDocker
(
Config
):
pass
...
...
This diff is collapsed.
Click to expand it.
conf_example.py
View file @
08a5c616
...
...
@@ -9,10 +9,10 @@ BASE_DIR = os.path.dirname(__file__)
class
Config
:
"""
Coco config file
Coco config file
, coco also load config from server update setting below
"""
# 项目名称, 会用来向Jumpserver注册, 识别而已, 不能重复
#
APP_
NAME = "localhost"
# NAME = "localhost"
# Jumpserver项目的url, api请求注册会使用
# CORE_HOST = os.environ.get("CORE_HOST") or 'http://127.0.0.1:8080'
...
...
@@ -49,16 +49,19 @@ class Config:
# ASSET_LIST_SORT_BY = 'ip'
# 登录是否支持密码认证
#
SSH_
PASSWORD_AUTH = True
# PASSWORD_AUTH = True
# 登录是否支持秘钥认证
#
SSH_
PUBLIC_KEY_AUTH = True
# PUBLIC_KEY_AUTH = True
# 和Jumpserver 保持心跳时间间隔
# HEARTBEAT_INTERVAL = 5
# Admin的名字,出问题会提示给用户
# ADMINS = ''
COMMAND_STORAGE
=
{
"TYPE"
:
"server"
}
config
=
Config
()
This diff is collapsed.
Click to expand it.
requirements/requirements.txt
View file @
08a5c616
...
...
@@ -28,4 +28,5 @@ tornado==4.5.2
urllib3==1.22
wcwidth==0.1.7
werkzeug==0.12.2
jumpserver-python-sdk==0.0.23
jumpserver-python-sdk==0.0.26
jms-es-sdk
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