Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
liming6
sshd-tool
Commits
4c295cf4
Commit
4c295cf4
authored
Mar 21, 2026
by
liming6
Browse files
fix 修复文件名中有空格不能正常识别的bug
parent
1f27f718
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
106 additions
and
114 deletions
+106
-114
cmd/file-monitor/logic/logic_test.go
cmd/file-monitor/logic/logic_test.go
+9
-0
cmd/file-monitor/logic/sftp.go
cmd/file-monitor/logic/sftp.go
+71
-101
cmd/file-monitor/main.go
cmd/file-monitor/main.go
+20
-13
cmd/file-monitor/readme.md
cmd/file-monitor/readme.md
+6
-0
No files found.
cmd/file-monitor/logic/logic_test.go
View file @
4c295cf4
...
@@ -69,3 +69,12 @@ func TestPids(t *testing.T) {
...
@@ -69,3 +69,12 @@ func TestPids(t *testing.T) {
tt
:=
time
.
Since
(
start
)
tt
:=
time
.
Since
(
start
)
t
.
Logf
(
"%d ms"
,
tt
.
Milliseconds
())
t
.
Logf
(
"%d ms"
,
tt
.
Milliseconds
())
}
}
func
TestParseSftpLog
(
t
*
testing
.
T
)
{
sla
,
err
:=
ParseSftpLog
(
`Mar 17 16:38:23 anolis sftp-server[62785]: close "/tmp/Everything-1.4.1.1030.x64-Setup. exe.tabby-upload" bytes read 0 written 1987848`
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
op
:=
sla
.
(
*
SftpLogClose
)
t
.
Logf
(
"%+v"
,
op
)
}
cmd/file-monitor/logic/sftp.go
View file @
4c295cf4
...
@@ -23,6 +23,11 @@ var (
...
@@ -23,6 +23,11 @@ var (
SftpLogMap
=
make
(
map
[
int32
]
*
SftpLogSet
)
// 全局的,用于记录sftp信息的map,key为进程id,value为日志集
SftpLogMap
=
make
(
map
[
int32
]
*
SftpLogSet
)
// 全局的,用于记录sftp信息的map,key为进程id,value为日志集
SftpLogLock
=
sync
.
RWMutex
{}
// 保护SftpLogMap的锁
SftpLogLock
=
sync
.
RWMutex
{}
// 保护SftpLogMap的锁
RegOpenFile
=
regexp
.
MustCompile
(
`(?i)^open "(.*)" flags (.*) mode (.*)$`
)
RegCloseFile
=
regexp
.
MustCompile
(
`(?i)^close "(.*)" bytes read (\d+) written (\d+)$`
)
RegRenameFile
=
regexp
.
MustCompile
(
`(?i)^rename old "(.*)" new "(.*)"$`
)
RegForceCloseFile
=
regexp
.
MustCompile
(
`(?i)^forced close "(.*)" bytes read (\d+) written (\d+)$`
)
)
)
type
GetSLA
interface
{
type
GetSLA
interface
{
...
@@ -44,131 +49,91 @@ const (
...
@@ -44,131 +49,91 @@ const (
SLACloseSession
SftpLogAction
=
"session closed"
SLACloseSession
SftpLogAction
=
"session closed"
)
)
func
parseSLA
(
s
string
,
t
time
.
Time
)
GetSLA
{
func
parseSLA
(
s
string
,
t
time
.
Time
)
(
GetSLA
,
error
)
{
p
re
v
:=
""
re
rr
:=
errors
.
New
(
"error sftp log format"
)
switch
{
switch
{
case
strings
.
HasPrefix
(
s
,
string
(
SLAOpen
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLAOpen
))
:
fields
:=
strings
.
Fields
(
s
)
fields
:=
RegOpenFile
.
FindStringSubmatch
(
s
)
if
len
(
fields
)
!=
4
{
return
nil
,
rerr
}
result
:=
SftpLogOpen
{
Time
:
t
}
result
:=
SftpLogOpen
{
Time
:
t
}
for
i
,
v
:=
range
fields
[
1
:
]
{
result
.
Path
=
fields
[
1
]
if
i
==
0
{
result
.
Flags
=
strings
.
Split
(
fields
[
2
],
","
)
result
.
Path
=
strings
.
Trim
(
v
,
`"`
)
p
,
err
:=
strconv
.
ParseUint
(
fields
[
3
],
8
,
32
)
continue
if
err
==
nil
{
}
result
.
Mode
=
mo
.
None
[
uint32
]()
switch
v
{
}
else
{
case
"flags"
,
"mode"
:
result
.
Mode
=
mo
.
Some
(
uint32
(
p
))
prev
=
v
default
:
switch
prev
{
case
"flags"
:
result
.
Flags
=
strings
.
Split
(
v
,
","
)
case
"mode"
:
p
,
err
:=
strconv
.
ParseUint
(
v
,
8
,
32
)
if
err
==
nil
{
result
.
Mode
=
mo
.
None
[
uint32
]()
}
else
{
result
.
Mode
=
mo
.
Some
(
uint32
(
p
))
}
}
}
}
}
return
&
result
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLAClose
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLAClose
))
:
fields
:=
strings
.
Fields
(
s
)
fields
:=
RegCloseFile
.
FindStringSubmatch
(
s
)
if
len
(
fields
)
!=
4
{
return
nil
,
rerr
}
result
:=
SftpLogClose
{
Time
:
t
}
result
:=
SftpLogClose
{
Time
:
t
}
for
k
,
v
:=
range
fields
[
1
:
]
{
result
.
Path
=
fields
[
1
]
if
k
==
0
{
read
,
err
:=
strconv
.
ParseUint
(
fields
[
2
],
10
,
64
)
result
.
Path
=
strings
.
Trim
(
v
,
`"`
)
if
err
!=
nil
{
}
result
.
Read
=
mo
.
None
[
uint64
]()
switch
v
{
}
else
{
case
"bytes"
,
"read"
,
"written"
:
result
.
Read
=
mo
.
Some
(
read
)
prev
=
v
default
:
switch
prev
{
case
"read"
:
read
,
err
:=
strconv
.
ParseUint
(
v
,
10
,
64
)
if
err
!=
nil
{
result
.
Read
=
mo
.
None
[
uint64
]()
}
else
{
result
.
Read
=
mo
.
Some
(
read
)
}
case
"written"
:
read
,
err
:=
strconv
.
ParseUint
(
v
,
10
,
64
)
if
err
!=
nil
{
result
.
Write
=
mo
.
None
[
uint64
]()
}
else
{
result
.
Write
=
mo
.
Some
(
read
)
}
}
}
}
}
return
&
result
write
,
err
:=
strconv
.
ParseUint
(
fields
[
3
],
10
,
64
)
if
err
!=
nil
{
result
.
Write
=
mo
.
None
[
uint64
]()
}
else
{
result
.
Write
=
mo
.
Some
(
write
)
}
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLARemove
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLARemove
))
:
fields
:=
strings
.
Fields
(
s
)
result
:=
SftpLogRemove
{
Time
:
t
}
result
:=
SftpLogRemove
{
Time
:
t
}
result
.
Path
=
fields
[
2
]
result
.
Path
=
strings
.
Trim
(
strings
.
Trim
(
s
,
string
(
SLARemove
)),
`"`
)
return
&
result
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLARename
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLARename
))
:
fields
:=
strings
.
Fields
(
s
)
fields
:=
RegRenameFile
.
FindStringSubmatch
(
s
)
result
:=
SftpLogRename
{
Time
:
t
}
if
len
(
fields
)
!=
3
{
for
_
,
v
:=
range
fields
[
1
:
]
{
return
nil
,
rerr
switch
v
{
case
"old"
,
"new"
:
prev
=
v
default
:
switch
prev
{
case
"old"
:
result
.
Old
=
strings
.
Trim
(
v
,
`"`
)
case
"new"
:
result
.
New
=
strings
.
Trim
(
v
,
`"`
)
}
}
}
}
return
&
result
result
:=
SftpLogRename
{
Time
:
t
}
result
.
Old
=
fields
[
1
]
result
.
New
=
fields
[
2
]
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLAForceClose
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLAForceClose
))
:
fields
:=
strings
.
Fields
(
s
)
fields
:=
RegForceCloseFile
.
FindStringSubmatch
(
s
)
if
len
(
fields
)
!=
4
{
return
nil
,
rerr
}
result
:=
SftpLogForceClose
{
Time
:
t
}
result
:=
SftpLogForceClose
{
Time
:
t
}
for
k
,
v
:=
range
fields
[
2
:
]
{
result
.
Path
=
fields
[
1
]
if
k
==
0
{
read
,
err
:=
strconv
.
ParseUint
(
fields
[
2
],
10
,
64
)
result
.
Path
=
strings
.
Trim
(
v
,
`"`
)
if
err
!=
nil
{
}
result
.
Read
=
mo
.
None
[
uint64
]()
switch
v
{
}
else
{
case
"bytes"
,
"read"
,
"written"
:
result
.
Read
=
mo
.
Some
(
read
)
prev
=
v
}
default
:
write
,
err
:=
strconv
.
ParseUint
(
fields
[
3
],
10
,
64
)
switch
prev
{
if
err
!=
nil
{
case
"read"
:
result
.
Write
=
mo
.
None
[
uint64
]()
read
,
err
:=
strconv
.
ParseUint
(
v
,
10
,
64
)
}
else
{
if
err
!=
nil
{
result
.
Write
=
mo
.
Some
(
write
)
result
.
Read
=
mo
.
None
[
uint64
]()
}
else
{
result
.
Read
=
mo
.
Some
(
read
)
}
case
"written"
:
read
,
err
:=
strconv
.
ParseUint
(
v
,
10
,
64
)
if
err
!=
nil
{
result
.
Write
=
mo
.
None
[
uint64
]()
}
else
{
result
.
Write
=
mo
.
Some
(
read
)
}
}
}
}
}
return
&
result
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLAOpenSession
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLAOpenSession
))
:
result
:=
SftpLogOpenSession
{
Time
:
t
}
result
:=
SftpLogOpenSession
{
Time
:
t
}
items
:=
RegParseSession
.
FindStringSubmatch
(
s
)
items
:=
RegParseSession
.
FindStringSubmatch
(
s
)
result
.
User
=
items
[
2
]
result
.
User
=
items
[
2
]
result
.
From
=
items
[
3
]
result
.
From
=
items
[
3
]
return
&
result
return
&
result
,
nil
case
strings
.
HasPrefix
(
s
,
string
(
SLACloseSession
))
:
case
strings
.
HasPrefix
(
s
,
string
(
SLACloseSession
))
:
result
:=
SftpLogCloseSession
{
Time
:
t
}
result
:=
SftpLogCloseSession
{
Time
:
t
}
items
:=
RegParseSession
.
FindStringSubmatch
(
s
)
items
:=
RegParseSession
.
FindStringSubmatch
(
s
)
result
.
User
=
items
[
2
]
result
.
User
=
items
[
2
]
result
.
From
=
items
[
3
]
result
.
From
=
items
[
3
]
return
&
result
return
&
result
,
nil
default
:
default
:
return
nil
return
nil
,
nil
}
}
}
}
...
@@ -588,6 +553,7 @@ func InsertAction(action GetSLA) {
...
@@ -588,6 +553,7 @@ func InsertAction(action GetSLA) {
}
}
}
}
// todo 需要适应文件名有空格的情况
func
ParseSftpLog
(
s
string
)
(
GetSLA
,
error
)
{
func
ParseSftpLog
(
s
string
)
(
GetSLA
,
error
)
{
if
!
strings
.
Contains
(
s
,
"sftp-server"
)
{
if
!
strings
.
Contains
(
s
,
"sftp-server"
)
{
// 不是sftp日志
// 不是sftp日志
...
@@ -617,7 +583,10 @@ func ParseSftpLog(s string) (GetSLA, error) {
...
@@ -617,7 +583,10 @@ func ParseSftpLog(s string) (GetSLA, error) {
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
fta
:=
parseSLA
(
fs
[
2
],
t
)
fta
,
err
:=
parseSLA
(
fs
[
2
],
t
)
if
err
!=
nil
{
return
nil
,
err
}
if
fta
==
nil
{
if
fta
==
nil
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -645,6 +614,7 @@ func StartSftpMonitor() {
...
@@ -645,6 +614,7 @@ func StartSftpMonitor() {
continue
continue
}
}
if
strings
.
Contains
(
items
[
1
],
"sftp-server"
)
{
if
strings
.
Contains
(
items
[
1
],
"sftp-server"
)
{
fmt
.
Println
(
items
[
1
])
l
,
err
:=
ParseSftpLog
(
items
[
1
])
l
,
err
:=
ParseSftpLog
(
items
[
1
])
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
...
...
cmd/file-monitor/main.go
View file @
4c295cf4
...
@@ -8,6 +8,7 @@ import (
...
@@ -8,6 +8,7 @@ import (
"time"
"time"
"github.com/gofrs/flock"
"github.com/gofrs/flock"
"github.com/spf13/pflag"
)
)
// import (
// import (
...
@@ -75,24 +76,20 @@ import (
...
@@ -75,24 +76,20 @@ import (
// }
// }
var
(
var
(
logfile
*
os
.
File
logfile
*
os
.
File
flagDebug
=
pflag
.
Bool
(
"debug"
,
false
,
"debug mode, print log to stdout, not file"
)
flagHelp
=
pflag
.
BoolP
(
"help"
,
"h"
,
false
,
"show usage"
)
)
)
func
init
()
{
func
main
()
{
logFile
,
err
:=
os
.
Create
(
fmt
.
Sprintf
(
"/var/log/file-monitor.%s.log"
,
time
.
Now
()
.
Format
(
"2006-01-02_15-04-05"
)))
pflag
.
Parse
()
if
err
==
nil
{
logfile
=
logFile
log
.
SetOutput
(
logFile
)
}
}
func
shutdown
()
{
if
*
flagHelp
{
if
logfile
!=
nil
{
fmt
.
Println
(
"Monitor sftp and scp file uploads. After a file is uploaded, use clamdscan to scan the file. If the file contains a virus, delete it"
)
logfile
.
Close
()
pflag
.
Usage
()
return
}
}
}
func
main
()
{
fileLock
:=
flock
.
New
(
"/tmp/file-monitor.lock"
)
fileLock
:=
flock
.
New
(
"/tmp/file-monitor.lock"
)
locked
,
err
:=
fileLock
.
TryLock
()
locked
,
err
:=
fileLock
.
TryLock
()
if
err
!=
nil
||
!
locked
{
if
err
!=
nil
||
!
locked
{
...
@@ -103,5 +100,15 @@ func main() {
...
@@ -103,5 +100,15 @@ func main() {
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Fatal
(
err
)
log
.
Fatal
(
err
)
}
}
if
!*
flagDebug
{
logFile
,
err
:=
os
.
Create
(
fmt
.
Sprintf
(
"/var/log/file-monitor.%s.log"
,
time
.
Now
()
.
Format
(
"2006-01-02_15-04-05"
)))
if
err
==
nil
{
logfile
=
logFile
log
.
SetOutput
(
logFile
)
defer
logFile
.
Close
()
}
}
logic
.
StartSftpMonitor
()
logic
.
StartSftpMonitor
()
}
}
cmd/file-monitor/readme.md
View file @
4c295cf4
...
@@ -15,3 +15,9 @@ file-minitor是一个监控sftp和scp上传文件动作并对上传文件进行
...
@@ -15,3 +15,9 @@ file-minitor是一个监控sftp和scp上传文件动作并对上传文件进行
同样大小下,
`.zip`
文件扫描比较慢,建议压缩包格式为
`.tar.gz`
同样大小下,
`.zip`
文件扫描比较慢,建议压缩包格式为
`.tar.gz`
## todo
添加白名单:
-
用户白名单:不扫描指定用户上传的文件
-
路径白名单:不扫描上传到指定路径的文件
\ 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