Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
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
koko
Commits
7e4e1989
Commit
7e4e1989
authored
May 28, 2019
by
Eric
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[update] add sftp server handler
parent
205be347
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
320 additions
and
15 deletions
+320
-15
config.go
pkg/config/config.go
+1
-0
sftp.go
pkg/handler/sftp.go
+319
-15
No files found.
pkg/config/config.go
View file @
7e4e1989
...
...
@@ -125,6 +125,7 @@ var Conf = &Config{
ReplayStorage
:
map
[
string
]
interface
{}{
"TYPE"
:
"server"
},
CommandStorage
:
map
[
string
]
interface
{}{
"TYPE"
:
"server"
},
UploadFailedReplay
:
true
,
SftpRoot
:
"/tmp"
,
}
func
SetConf
(
conf
*
Config
)
{
...
...
pkg/handler/sftp.go
View file @
7e4e1989
package
handler
import
(
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"time"
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
gossh
"golang.org/x/crypto/ssh"
"cocogo/pkg/cctx"
"cocogo/pkg/config"
"cocogo/pkg/logger"
"cocogo/pkg/model"
"cocogo/pkg/service"
)
func
SftpHandler
(
sess
ssh
.
Session
)
{
debugStream
:=
ioutil
.
Discard
serverOptions
:=
[]
sftp
.
ServerOption
{
sftp
.
WithDebug
(
debugStream
),
}
server
,
err
:=
sftp
.
NewServer
(
sess
,
serverOptions
...
,
)
if
err
!=
nil
{
logger
.
Errorf
(
"sftp server init error: %s"
,
err
)
return
}
if
err
:=
server
.
Serve
();
err
==
io
.
EOF
{
server
.
Close
()
ctx
,
cancel
:=
cctx
.
NewContext
(
sess
)
defer
cancel
()
userhandler
:=
&
userSftpRequests
{
user
:
ctx
.
User
()
}
userhandler
.
initial
()
hs
:=
sftp
.
Handlers
{
FileGet
:
userhandler
,
FilePut
:
userhandler
,
FileCmd
:
userhandler
,
FileList
:
userhandler
}
req
:=
sftp
.
NewRequestServer
(
sess
,
hs
)
if
err
:=
req
.
Serve
();
err
==
io
.
EOF
{
_
=
req
.
Close
()
logger
.
Info
(
"sftp client exited session."
)
}
else
if
err
!=
nil
{
logger
.
Error
(
"sftp server completed with error:"
,
err
)
}
}
type
userSftpRequests
struct
{
user
*
model
.
User
assets
model
.
AssetList
rootPath
string
// tmp || home || ~
hosts
map
[
string
]
*
HostNameDir
}
func
(
fs
*
userSftpRequests
)
initial
()
{
fs
.
loadAssets
()
fs
.
hosts
=
make
(
map
[
string
]
*
HostNameDir
)
fs
.
rootPath
=
config
.
GetConf
()
.
SftpRoot
for
_
,
item
:=
range
fs
.
assets
{
tmpDir
:=
&
HostNameDir
{
rootPath
:
fs
.
rootPath
,
hostname
:
item
.
Hostname
,
asset
:
&
item
,
time
:
time
.
Now
()
.
UTC
(),
}
fs
.
hosts
[
item
.
Hostname
]
=
tmpDir
}
}
func
(
fs
*
userSftpRequests
)
loadAssets
()
{
fs
.
assets
=
service
.
GetUserAssets
(
fs
.
user
.
ID
,
"1"
)
}
func
(
fs
*
userSftpRequests
)
Filelist
(
r
*
sftp
.
Request
)
(
sftp
.
ListerAt
,
error
)
{
var
fileInfos
=
listerat
{}
var
err
error
logger
.
Debug
(
"list path: "
,
r
.
Filepath
)
if
r
.
Filepath
==
"/"
{
for
_
,
v
:=
range
fs
.
hosts
{
fileInfos
=
append
(
fileInfos
,
v
)
}
logger
.
Debug
(
fileInfos
)
return
fileInfos
,
err
}
pathNames
:=
strings
.
Split
(
strings
.
TrimPrefix
(
r
.
Filepath
,
"/"
),
"/"
)
hostDir
,
ok
:=
fs
.
hosts
[
pathNames
[
0
]]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
hostDir
.
suMaps
==
nil
{
hostDir
.
suMaps
=
make
(
map
[
string
]
*
SysUserDir
)
systemUsers
:=
hostDir
.
asset
.
SystemUsers
for
i
,
sysUser
:=
range
systemUsers
{
hostDir
.
suMaps
[
sysUser
.
Name
]
=
&
SysUserDir
{
time
:
time
.
Now
()
.
UTC
(),
rootPath
:
fs
.
rootPath
,
systemUser
:
&
systemUsers
[
i
],
prefix
:
fmt
.
Sprintf
(
"/%s/%s"
,
hostDir
.
asset
.
Hostname
,
sysUser
.
Name
),
}
}
}
if
len
(
pathNames
)
==
1
{
for
_
,
v
:=
range
hostDir
.
suMaps
{
fileInfos
=
append
(
fileInfos
,
v
)
}
return
fileInfos
,
err
}
var
realPath
string
var
sysUserDir
*
SysUserDir
sysUserDir
,
ok
=
hostDir
.
suMaps
[
pathNames
[
1
]]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
realPath
=
sysUserDir
.
ParsePath
(
r
.
Filepath
)
if
sysUserDir
.
client
==
nil
{
sysUserDir
.
client
,
err
=
fs
.
GetSftpClient
(
hostDir
.
asset
,
sysUserDir
.
systemUser
)
if
err
!=
nil
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
}
fileInfos
,
err
=
sysUserDir
.
client
.
ReadDir
(
realPath
)
switch
r
.
Method
{
case
"List"
:
return
fileInfos
,
err
case
"Stat"
:
return
fileInfos
,
err
case
"Readlink"
:
return
fileInfos
,
err
}
return
fileInfos
,
err
}
func
(
fs
*
userSftpRequests
)
Filecmd
(
r
*
sftp
.
Request
)
error
{
logger
.
Debug
(
"File cmd: "
,
r
.
Filepath
)
pathNames
:=
strings
.
Split
(
strings
.
TrimPrefix
(
r
.
Filepath
,
"/"
),
"/"
)
if
len
(
pathNames
)
<=
2
{
return
sftp
.
ErrSshFxPermissionDenied
}
hostDir
:=
fs
.
hosts
[
pathNames
[
0
]]
suDir
:=
hostDir
.
suMaps
[
pathNames
[
1
]]
if
suDir
.
client
==
nil
{
client
,
err
:=
fs
.
GetSftpClient
(
hostDir
.
asset
,
suDir
.
systemUser
)
if
err
!=
nil
{
return
sftp
.
ErrSshFxPermissionDenied
}
suDir
.
client
=
client
}
realPathName
:=
suDir
.
ParsePath
(
r
.
Filepath
)
switch
r
.
Method
{
case
"Setstat"
:
return
nil
case
"Rename"
:
realNewName
:=
suDir
.
ParsePath
(
r
.
Target
)
return
suDir
.
client
.
Rename
(
realPathName
,
realNewName
)
case
"Rmdir"
:
return
suDir
.
client
.
RemoveDirectory
(
realPathName
)
case
"Remove"
:
return
suDir
.
client
.
Remove
(
realPathName
)
case
"Mkdir"
:
return
suDir
.
client
.
MkdirAll
(
realPathName
)
case
"Symlink"
:
realNewName
:=
suDir
.
ParsePath
(
r
.
Target
)
return
suDir
.
client
.
Symlink
(
realPathName
,
realNewName
)
}
return
nil
}
func
(
fs
*
userSftpRequests
)
Filewrite
(
r
*
sftp
.
Request
)
(
io
.
WriterAt
,
error
)
{
logger
.
Debug
(
"File write: "
,
r
.
Filepath
)
pathNames
:=
strings
.
Split
(
strings
.
TrimPrefix
(
r
.
Filepath
,
"/"
),
"/"
)
if
len
(
pathNames
)
<=
2
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
hostDir
:=
fs
.
hosts
[
pathNames
[
0
]]
suDir
:=
hostDir
.
suMaps
[
pathNames
[
1
]]
if
suDir
.
client
==
nil
{
client
,
err
:=
fs
.
GetSftpClient
(
hostDir
.
asset
,
suDir
.
systemUser
)
if
err
!=
nil
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
suDir
.
client
=
client
}
realPathName
:=
suDir
.
ParsePath
(
r
.
Filepath
)
f
,
err
:=
suDir
.
client
.
Create
(
realPathName
)
return
NewWriterAt
(
f
),
err
}
func
(
fs
*
userSftpRequests
)
Fileread
(
r
*
sftp
.
Request
)
(
io
.
ReaderAt
,
error
)
{
logger
.
Debug
(
"File read: "
,
r
.
Filepath
)
pathNames
:=
strings
.
Split
(
strings
.
TrimPrefix
(
r
.
Filepath
,
"/"
),
"/"
)
if
len
(
pathNames
)
<=
2
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
hostDir
:=
fs
.
hosts
[
pathNames
[
0
]]
suDir
:=
hostDir
.
suMaps
[
pathNames
[
1
]]
if
suDir
.
client
==
nil
{
client
,
err
:=
fs
.
GetSftpClient
(
hostDir
.
asset
,
suDir
.
systemUser
)
if
err
!=
nil
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
suDir
.
client
=
client
}
realPathName
:=
suDir
.
ParsePath
(
r
.
Filepath
)
f
,
err
:=
suDir
.
client
.
Open
(
realPathName
)
if
err
!=
nil
{
return
nil
,
err
}
return
NewReaderAt
(
f
),
err
}
func
(
fs
*
userSftpRequests
)
GetSftpClient
(
asset
*
model
.
Asset
,
sysUser
*
model
.
SystemUser
)
(
*
sftp
.
Client
,
error
)
{
logger
.
Debug
(
"Get Sftp Client"
)
info
:=
service
.
GetSystemUserAssetAuthInfo
(
sysUser
.
Id
,
asset
.
Id
)
return
CreateSFTPConn
(
sysUser
.
Username
,
info
.
Password
,
info
.
PrivateKey
,
asset
.
Ip
,
strconv
.
Itoa
(
asset
.
Port
))
}
type
HostNameDir
struct
{
rootPath
string
hostname
string
time
time
.
Time
asset
*
model
.
Asset
suMaps
map
[
string
]
*
SysUserDir
}
func
(
h
*
HostNameDir
)
Name
()
string
{
return
h
.
hostname
}
func
(
h
*
HostNameDir
)
Size
()
int64
{
return
int64
(
0
)
}
func
(
h
*
HostNameDir
)
Mode
()
os
.
FileMode
{
return
os
.
FileMode
(
0400
)
|
os
.
ModeDir
}
func
(
h
*
HostNameDir
)
ModTime
()
time
.
Time
{
return
h
.
time
}
func
(
h
*
HostNameDir
)
IsDir
()
bool
{
return
true
}
func
(
h
*
HostNameDir
)
Sys
()
interface
{}
{
// fake current dir sys info
fakeInfo
,
_
:=
os
.
Stat
(
"."
)
return
fakeInfo
.
Sys
()
}
type
SysUserDir
struct
{
ID
string
prefix
string
rootPath
string
systemUser
*
model
.
SystemUser
time
time
.
Time
client
*
sftp
.
Client
}
func
(
su
*
SysUserDir
)
Name
()
string
{
return
su
.
systemUser
.
Name
}
func
(
su
*
SysUserDir
)
Size
()
int64
{
return
int64
(
0
)
}
func
(
su
*
SysUserDir
)
Mode
()
os
.
FileMode
{
return
os
.
FileMode
(
0400
)
|
os
.
ModeDir
}
func
(
su
*
SysUserDir
)
ModTime
()
time
.
Time
{
return
su
.
time
}
func
(
su
*
SysUserDir
)
IsDir
()
bool
{
return
true
}
func
(
su
*
SysUserDir
)
Sys
()
interface
{}
{
// fake current dir sys info
fakeInfo
,
_
:=
os
.
Stat
(
"."
)
return
fakeInfo
.
Sys
()
}
func
(
su
*
SysUserDir
)
ParsePath
(
path
string
)
string
{
var
realPath
string
realPath
=
strings
.
ReplaceAll
(
path
,
su
.
prefix
,
su
.
rootPath
)
logger
.
Debug
(
"real path: "
,
realPath
)
return
realPath
}
type
listerat
[]
os
.
FileInfo
func
(
f
listerat
)
ListAt
(
ls
[]
os
.
FileInfo
,
offset
int64
)
(
int
,
error
)
{
var
n
int
if
offset
>=
int64
(
len
(
f
))
{
return
0
,
io
.
EOF
}
n
=
copy
(
ls
,
f
[
offset
:
])
if
n
<
len
(
ls
)
{
return
n
,
io
.
EOF
}
return
n
,
nil
}
func
NewWriterAt
(
f
*
sftp
.
File
)
io
.
WriterAt
{
return
&
clientReadWritAt
{
f
:
f
}
}
func
NewReaderAt
(
f
*
sftp
.
File
)
io
.
ReaderAt
{
return
&
clientReadWritAt
{
f
:
f
}
}
type
clientReadWritAt
struct
{
f
*
sftp
.
File
}
func
(
c
*
clientReadWritAt
)
WriteAt
(
p
[]
byte
,
off
int64
)
(
n
int
,
err
error
)
{
return
c
.
f
.
Write
(
p
)
}
func
(
c
*
clientReadWritAt
)
ReadAt
(
p
[]
byte
,
off
int64
)
(
n
int
,
err
error
)
{
return
c
.
f
.
Read
(
p
)
}
func
CreateSFTPConn
(
user
,
password
,
privateKey
,
host
,
port
string
)
(
*
sftp
.
Client
,
error
)
{
authMethods
:=
make
([]
gossh
.
AuthMethod
,
0
)
if
password
!=
""
{
authMethods
=
append
(
authMethods
,
gossh
.
Password
(
password
))
}
if
privateKey
!=
""
{
if
signer
,
err
:=
gossh
.
ParsePrivateKey
([]
byte
(
privateKey
));
err
!=
nil
{
err
=
fmt
.
Errorf
(
"parse private key error: %sc"
,
err
)
}
else
{
authMethods
=
append
(
authMethods
,
gossh
.
PublicKeys
(
signer
))
}
}
config
:=
&
gossh
.
ClientConfig
{
User
:
user
,
Auth
:
authMethods
,
HostKeyCallback
:
gossh
.
InsecureIgnoreHostKey
(),
Timeout
:
300
*
time
.
Second
,
}
client
,
err
:=
gossh
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
host
,
port
),
config
)
if
err
!=
nil
{
return
nil
,
err
}
return
sftp
.
NewClient
(
client
)
}
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