支持记录SFTP操作日志了,能记录文件打开、删除,目录创建、删除,改名以及创建符号链接等操作。

pull/32/head
Apex Liu 2017-05-27 23:51:20 +08:00
parent 428dc323f3
commit ef23397be8
5 changed files with 1558 additions and 1306 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,133 +1,134 @@
#ifndef __SSH_SESSION_H__
#define __SSH_SESSION_H__
#include "ssh_recorder.h"
#include <ex.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#include <libssh/sftp.h>
#include <vector>
#include <list>
#define TS_SSH_CHANNEL_TYPE_UNKNOWN 0
#define TS_SSH_CHANNEL_TYPE_SHELL 1
#define TS_SSH_CHANNEL_TYPE_SFTP 2
#define TS_SSH_DATA_FROM_CLIENT 1
#define TS_SSH_DATA_FROM_SERVER 2
typedef struct TS_SSH_CHANNEL_INFO
{
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
ssh_channel channel;
TS_SSH_CHANNEL_INFO()
{
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
channel = NULL;
}
}TS_SSH_CHANNEL_INFO;
typedef std::map<ssh_channel, TS_SSH_CHANNEL_INFO*> ts_ssh_channel_map;
class SshProxy;
class SshSession : public ExThreadBase
{
public:
SshSession(SshProxy* proxy, ssh_session sess_client);
virtual ~SshSession();
SshProxy* get_proxy(void) { return m_proxy; }
TS_SSH_CHANNEL_INFO* _get_cli_channel(ssh_channel srv_channel);
TS_SSH_CHANNEL_INFO* _get_srv_channel(ssh_channel cli_channel);
void client_ip(const char* ip) { m_client_ip = ip; }
const char* client_ip(void) const { return m_client_ip.c_str(); }
void client_port(ex_u16 port) { m_client_port = port; }
ex_u16 client_port(void) const { return m_client_port; }
protected:
// 继承自 TppSessionBase
bool _on_session_begin(const TPP_SESSION_INFO* info);
bool _on_session_end(void);
void _thread_loop(void);
void _set_stop_flag(void);
void _process_command(int from, const ex_u8* data, int len);
private:
void _run(void);
void _close_channels(void);
void _enter_sftp_mode(void);
static int _on_auth_password_request(ssh_session session, const char *user, const char *password, void *userdata);
static ssh_channel _on_new_channel_request(ssh_session session, void *userdata);
static int _on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata);
static int _on_client_shell_request(ssh_session session, ssh_channel channel, void *userdata);
static void _on_client_channel_close(ssh_session session, ssh_channel channel, void* userdata);
static int _on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
static int _on_client_pty_win_change(ssh_session session, ssh_channel channel, int width, int height, int pxwidth, int pwheight, void *userdata);
static int _on_client_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata);
static int _on_client_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata);
static int _on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
static void _on_server_channel_close(ssh_session session, ssh_channel channel, void* userdata);
private:
int m_retcode;
int m_db_id;
TppSshRec m_rec;
SshProxy* m_proxy;
ssh_session m_cli_session;
ssh_session m_srv_session;
ExThreadLock m_lock;
ex_astr m_client_ip;
ex_u16 m_client_port;
ex_astr m_sid;
ex_astr m_server_ip;
ex_u16 m_server_port;
ex_astr m_user_name;
ex_astr m_user_auth;
int m_auth_mode;
bool m_is_first_server_data;
bool m_is_sftp;
bool m_is_logon;
// 一个ssh_session中可以打开多个ssh_channel
ts_ssh_channel_map m_channel_cli_srv; // 通过客户端通道查找服务端通道
ts_ssh_channel_map m_channel_srv_cli; // 通过服务端通道查找客户端通道
bool m_have_error;
bool m_recving_from_srv; // 是否正在从服务器接收数据?
bool m_recving_from_cli; // 是否正在从客户端接收数据?
struct ssh_server_callbacks_struct m_srv_cb;
struct ssh_channel_callbacks_struct m_cli_channel_cb;
struct ssh_channel_callbacks_struct m_srv_channel_cb;
int m_command_flag;
std::list<char> m_cmd_char_list;
std::list<char>::iterator m_cmd_char_pos;
};
#endif // __SSH_SESSION_H__
#ifndef __SSH_SESSION_H__
#define __SSH_SESSION_H__
#include "ssh_recorder.h"
#include <ex.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <libssh/callbacks.h>
#include <libssh/sftp.h>
#include <vector>
#include <list>
#define TS_SSH_CHANNEL_TYPE_UNKNOWN 0
#define TS_SSH_CHANNEL_TYPE_SHELL 1
#define TS_SSH_CHANNEL_TYPE_SFTP 2
#define TS_SSH_DATA_FROM_CLIENT 1
#define TS_SSH_DATA_FROM_SERVER 2
typedef struct TS_SSH_CHANNEL_INFO
{
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
ssh_channel channel;
TS_SSH_CHANNEL_INFO()
{
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
channel = NULL;
}
}TS_SSH_CHANNEL_INFO;
typedef std::map<ssh_channel, TS_SSH_CHANNEL_INFO*> ts_ssh_channel_map;
class SshProxy;
class SshSession : public ExThreadBase
{
public:
SshSession(SshProxy* proxy, ssh_session sess_client);
virtual ~SshSession();
SshProxy* get_proxy(void) { return m_proxy; }
TS_SSH_CHANNEL_INFO* _get_cli_channel(ssh_channel srv_channel);
TS_SSH_CHANNEL_INFO* _get_srv_channel(ssh_channel cli_channel);
void client_ip(const char* ip) { m_client_ip = ip; }
const char* client_ip(void) const { return m_client_ip.c_str(); }
void client_port(ex_u16 port) { m_client_port = port; }
ex_u16 client_port(void) const { return m_client_port; }
protected:
// 继承自 TppSessionBase
bool _on_session_begin(const TPP_SESSION_INFO* info);
bool _on_session_end(void);
void _thread_loop(void);
void _set_stop_flag(void);
void _process_ssh_command(int from, const ex_u8* data, int len);
void _process_sftp_command(const ex_u8* data, int len);
private:
void _run(void);
void _close_channels(void);
void _enter_sftp_mode(void);
static int _on_auth_password_request(ssh_session session, const char *user, const char *password, void *userdata);
static ssh_channel _on_new_channel_request(ssh_session session, void *userdata);
static int _on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata);
static int _on_client_shell_request(ssh_session session, ssh_channel channel, void *userdata);
static void _on_client_channel_close(ssh_session session, ssh_channel channel, void* userdata);
static int _on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
static int _on_client_pty_win_change(ssh_session session, ssh_channel channel, int width, int height, int pxwidth, int pwheight, void *userdata);
static int _on_client_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata);
static int _on_client_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata);
static int _on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
static void _on_server_channel_close(ssh_session session, ssh_channel channel, void* userdata);
private:
int m_retcode;
int m_db_id;
TppSshRec m_rec;
SshProxy* m_proxy;
ssh_session m_cli_session;
ssh_session m_srv_session;
ExThreadLock m_lock;
ex_astr m_client_ip;
ex_u16 m_client_port;
ex_astr m_sid;
ex_astr m_server_ip;
ex_u16 m_server_port;
ex_astr m_user_name;
ex_astr m_user_auth;
int m_auth_mode;
bool m_is_first_server_data;
bool m_is_sftp;
bool m_is_logon;
// 一个ssh_session中可以打开多个ssh_channel
ts_ssh_channel_map m_channel_cli_srv; // 通过客户端通道查找服务端通道
ts_ssh_channel_map m_channel_srv_cli; // 通过服务端通道查找客户端通道
bool m_have_error;
bool m_recving_from_srv; // 是否正在从服务器接收数据?
bool m_recving_from_cli; // 是否正在从客户端接收数据?
struct ssh_server_callbacks_struct m_srv_cb;
struct ssh_channel_callbacks_struct m_cli_channel_cb;
struct ssh_channel_callbacks_struct m_srv_channel_cb;
int m_command_flag;
std::list<char> m_cmd_char_list;
std::list<char>::iterator m_cmd_char_pos;
};
#endif // __SSH_SESSION_H__

View File

@ -80,10 +80,20 @@ class ReplayStaticFileHandler(tornado.web.StaticFileHandler):
class ComandLogHandler(TPBaseAdminAuthHandler):
def get(self, protocol, record_id):
header = record.read_record_head(record_id)
if header is None:
return self.write_json(-3, '操作失败')
# ret = dict()
# ret['header'] = header
# return self.write_json(0, data=ret)
param = dict()
param['header'] = header
param['count'] = 0
param['op'] = list()
cmd_type = 0 # 0 = ssh, 1 = sftp
protocol = int(protocol)
if protocol == 1:
pass
@ -94,12 +104,26 @@ class ComandLogHandler(TPBaseAdminAuthHandler):
file = open(file_info, 'r')
data = file.readlines()
for i in range(len(data)):
param['op'].append({'t': data[i][1:20], 'c': data[i][22:-1]})
if 0 == i:
cmd = data[i][22:-1]
if 'SFTP INITIALIZE' == cmd:
cmd_type = 1
continue
if cmd_type == 0:
param['op'].append({'t': data[i][1:20], 'c': data[i][22:-1]})
else:
cmd_info = data[i][22:-1].split(':')
if len(cmd_info) != 4:
continue
param['op'].append({'t': data[i][1:20], 'c': cmd_info[0], 'p1': cmd_info[2], 'p2': cmd_info[3]})
except:
pass
param['count'] = len(param['op'])
self.render('log/record-ssh-cmd.mako', page_param=json.dumps(param))
if cmd_type == 0:
self.render('log/record-ssh-cmd.mako', page_param=json.dumps(param))
else:
self.render('log/record-sftp-cmd.mako', page_param=json.dumps(param))
class RecordGetHeader(TPBaseAdminAuthJsonHandler):

View File

@ -0,0 +1,125 @@
<%!
page_title_ = 'SFTP操作记录'
%>
<%inherit file="../page_no_sidebar_base.mako"/>
<%block name="extend_js">
</%block>
<%block name="breadcrumb">
<ol class="breadcrumb">
<li><i class="fa fa-file-text-o"></i> ${self.attr.page_title_}</li>
<li><span id="recorder-info"></span></li>
</ol>
</%block>
<%block name="extend_css">
<style type="text/css">
#no-op-msg {
display: none;
padding: 20px;
margin: 50px;
background-color: #fffed5;
border-radius: 5px;
font-size: 120%;
}
#op-list {
display: none;
padding: 20px;
margin: 20px 10px 20px 10px;
background-color: #ffffff;
font-size: 14px;
border-radius: 5px;
}
.op-item {
margin-bottom: 3px;
}
.time, .cmd, .path {
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
font-size:13px;
line-height: 15px;
padding: 0 5px;
border-radius: 3px;
}
.time {
margin-right: 15px;
background-color: #d8d8d8;
}
.path {
margin:0 5px 0 5px;
}
.cmd-danger {
background-color: #ffbba6;
font-weight: bold;
}
.cmd-info {
background-color: #b4fdb1;
}
</style>
</%block>
<div class="page-content">
<div id="no-op-msg">
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
</div>
<div id="op-list"></div>
</div>
<%block name="embed_js">
<script type="text/javascript">
ywl.add_page_options(${page_param});
ywl.on_init = function (cb_stack, cb_args) {
if (ywl.page_options.count === 0) {
$('#no-op-msg').show();
} else {
var header = ywl.page_options.header;
$('#recorder-info').html(header.account + ' 于 ' + format_datetime(header.start) + ' 访问 ' + header.user_name + '@' + header.ip + ':' + header.port);
var dom_op_list = $('#op-list');
var html = [];
for (var i = 0; i < ywl.page_options.count; i++) {
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> ');
if (ywl.page_options.op[i].c === '3') {
html.push('<span class="cmd">打开文件</span>');
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
} else if (ywl.page_options.op[i].c === '13') {
html.push('<span class="cmd cmd-danger">删除文件</span>');
html.push('<span class="path cmd-danger">' + ywl.page_options.op[i].p1 + '</span>');
} else if (ywl.page_options.op[i].c === '14') {
html.push('<span class="cmd">创建目录</span>');
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
} else if (ywl.page_options.op[i].c === '15') {
html.push('<span class="cmd cmd-danger">删除目录</span>');
html.push('<span class="path cmd-danger">' + ywl.page_options.op[i].p1 + '</span>');
} else if (ywl.page_options.op[i].c === '18') {
html.push('<span class="cmd cmd-info">更改名称</span>');
html.push('<span class="path cmd-info">' + ywl.page_options.op[i].p1 + '</span>');
html.push('<i class="fa fa-arrow-circle-right"></i>');
html.push('<span class="path cmd-info">' + ywl.page_options.op[i].p2 + '</span>');
} else if (ywl.page_options.op[i].c === '21') {
html.push('<span class="cmd">创建链接</span>');
html.push('<span class="path">' + ywl.page_options.op[i].p2 + '</span>');
html.push('<i class="fa fa-arrow-right"></i>');
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
}
html.push('</div>');
}
dom_op_list.append(html.join(''));
dom_op_list.show();
}
cb_stack.exec();
};
</script>
</%block>

View File

@ -1,101 +1,105 @@
<%!
page_title_ = '操作记录'
%>
<%inherit file="../page_no_sidebar_base.mako"/>
<%block name="extend_js">
</%block>
<%block name="breadcrumb">
<ol class="breadcrumb">
<li><i class="fa fa-server"></i> ${self.attr.page_title_}</li>
</ol>
</%block>
<%block name="extend_css">
<style type="text/css">
#no-op-msg {
display: none;
padding: 20px;
margin: 50px;
background-color: #fffed5;
border-radius: 5px;
font-size: 120%;
}
#op-list {
display: none;
padding: 20px;
margin: 20px 10px 20px 10px;
background-color: #ffffff;
font-size: 14px;
border-radius: 5px;
}
.op-item {
margin-bottom: 3px;
}
.time, .cmd {
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
line-height: 15px;
padding: 0 5px;
border-radius: 3px;
}
.time {
margin-right: 15px;
background-color: #d8d8d8;
}
.cmd-danger {
background-color: #ffbba6;
font-weight: bold;
}
.cmd-info {
background-color: #b4fdb1;
}
</style>
</%block>
<div class="page-content">
<div id="no-op-msg">
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
</div>
<div id="op-list"></div>
</div>
<%block name="embed_js">
<script type="text/javascript">
ywl.add_page_options(${page_param});
var danger_cmd = ['chmod', 'chown', 'kill', 'rm', 'su', 'sudo'];
var info_cmd = ['exit'];
ywl.on_init = function (cb_stack, cb_args) {
if (ywl.page_options.count == 0) {
$('#no-op-msg').show();
} else {
var dom_op_list = $('#op-list');
var html = [];
for (var i = 0; i < ywl.page_options.count; i++) {
var cmd_list = ywl.page_options.op[i].c.split(' ');
var cmd_class = '';
if (_.intersection(cmd_list, danger_cmd).length > 0) {
cmd_class = ' cmd-danger';
} else if (_.intersection(cmd_list, info_cmd).length > 0) {
cmd_class = ' cmd-info';
}
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> <span class="cmd' + cmd_class + '">' + ywl.page_options.op[i].c + '</span></li></div>');
}
dom_op_list.append(html.join(''));
dom_op_list.show();
}
cb_stack.exec();
};
</script>
<%!
page_title_ = 'SSH操作记录'
%>
<%inherit file="../page_no_sidebar_base.mako"/>
<%block name="extend_js">
</%block>
<%block name="breadcrumb">
<ol class="breadcrumb">
<li><i class="fa fa-file-text-o"></i> ${self.attr.page_title_}</li>
<li><span id="recorder-info"></span></li>
</ol>
</%block>
<%block name="extend_css">
<style type="text/css">
#no-op-msg {
display: none;
padding: 20px;
margin: 50px;
background-color: #fffed5;
border-radius: 5px;
font-size: 120%;
}
#op-list {
display: none;
padding: 20px;
margin: 20px 10px 20px 10px;
background-color: #ffffff;
font-size: 14px;
border-radius: 5px;
}
.op-item {
margin-bottom: 3px;
}
.time, .cmd {
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
line-height: 15px;
padding: 0 5px;
border-radius: 3px;
}
.time {
margin-right: 15px;
background-color: #d8d8d8;
}
.cmd-danger {
background-color: #ffbba6;
font-weight: bold;
}
.cmd-info {
background-color: #b4fdb1;
}
</style>
</%block>
<div class="page-content">
<div id="no-op-msg">
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
</div>
<div id="op-list"></div>
</div>
<%block name="embed_js">
<script type="text/javascript">
ywl.add_page_options(${page_param});
var danger_cmd = ['chmod', 'chown', 'kill', 'rm', 'su', 'sudo'];
var info_cmd = ['exit'];
ywl.on_init = function (cb_stack, cb_args) {
if (ywl.page_options.count === 0) {
$('#no-op-msg').show();
} else {
var header = ywl.page_options.header;
$('#recorder-info').html(header.account + ' 于 ' + format_datetime(header.start) + ' 访问 ' + header.user_name + '@' + header.ip + ':' + header.port);
var dom_op_list = $('#op-list');
var html = [];
for (var i = 0; i < ywl.page_options.count; i++) {
var cmd_list = ywl.page_options.op[i].c.split(' ');
var cmd_class = '';
if (_.intersection(cmd_list, danger_cmd).length > 0) {
cmd_class = ' cmd-danger';
} else if (_.intersection(cmd_list, info_cmd).length > 0) {
cmd_class = ' cmd-info';
}
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> <span class="cmd' + cmd_class + '">' + ywl.page_options.op[i].c + '</span></div>');
}
dom_op_list.append(html.join(''));
dom_op_list.show();
}
cb_stack.exec();
};
</script>
</%block>