mirror of
https://github.com/fatedier/frp.git
synced 2026-04-14 04:59:11 +08:00
feat: ssh client implement (#3671)
* feat: frps support ssh * fix: comments * fix: update pkg * fix: remove useless change --------- Co-authored-by: int7 <int7@gmail.com>
This commit is contained in:
185
pkg/ssh/vclient.go
Normal file
185
pkg/ssh/vclient.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/fatedier/frp/pkg/config"
|
||||
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||
"github.com/fatedier/frp/pkg/msg"
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
frp_net "github.com/fatedier/frp/pkg/util/net"
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
"github.com/fatedier/frp/pkg/util/xlog"
|
||||
"github.com/fatedier/frp/server/controller"
|
||||
"github.com/fatedier/frp/server/proxy"
|
||||
)
|
||||
|
||||
// VirtualService is a client VirtualService run in frps
|
||||
type VirtualService struct {
|
||||
clientCfg v1.ClientCommonConfig
|
||||
pxyCfg v1.ProxyConfigurer
|
||||
serverCfg v1.ServerConfig
|
||||
|
||||
sshSvc *Service
|
||||
|
||||
// uniq id got from frps, attach it in loginMsg
|
||||
runID string
|
||||
loginMsg *msg.Login
|
||||
|
||||
// All resource managers and controllers
|
||||
rc *controller.ResourceController
|
||||
|
||||
exit uint32 // 0 means not exit
|
||||
// SSHService context
|
||||
ctx context.Context
|
||||
// call cancel to stop SSHService
|
||||
cancel context.CancelFunc
|
||||
|
||||
replyCh chan interface{}
|
||||
pxy proxy.Proxy
|
||||
}
|
||||
|
||||
func NewVirtualService(
|
||||
ctx context.Context,
|
||||
clientCfg v1.ClientCommonConfig,
|
||||
serverCfg v1.ServerConfig,
|
||||
logMsg msg.Login,
|
||||
rc *controller.ResourceController,
|
||||
pxyCfg v1.ProxyConfigurer,
|
||||
sshSvc *Service,
|
||||
replyCh chan interface{},
|
||||
) (svr *VirtualService, err error) {
|
||||
svr = &VirtualService{
|
||||
clientCfg: clientCfg,
|
||||
serverCfg: serverCfg,
|
||||
rc: rc,
|
||||
|
||||
loginMsg: &logMsg,
|
||||
|
||||
sshSvc: sshSvc,
|
||||
pxyCfg: pxyCfg,
|
||||
|
||||
ctx: ctx,
|
||||
exit: 0,
|
||||
|
||||
replyCh: replyCh,
|
||||
}
|
||||
|
||||
svr.runID, err = util.RandID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go svr.loopCheck()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (svr *VirtualService) Run(ctx context.Context) (err error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
svr.ctx = xlog.NewContext(ctx, xlog.New())
|
||||
svr.cancel = cancel
|
||||
|
||||
remoteAddr, err := svr.RegisterProxy(&msg.NewProxy{
|
||||
ProxyName: svr.pxyCfg.(*v1.TCPProxyConfig).Name,
|
||||
ProxyType: svr.pxyCfg.(*v1.TCPProxyConfig).Type,
|
||||
RemotePort: svr.pxyCfg.(*v1.TCPProxyConfig).RemotePort,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("run a reverse proxy on port: %v", remoteAddr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svr *VirtualService) Close() {
|
||||
svr.GracefulClose(time.Duration(0))
|
||||
}
|
||||
|
||||
func (svr *VirtualService) GracefulClose(d time.Duration) {
|
||||
atomic.StoreUint32(&svr.exit, 1)
|
||||
svr.pxy.Close()
|
||||
|
||||
if svr.cancel != nil {
|
||||
svr.cancel()
|
||||
}
|
||||
|
||||
svr.replyCh <- &VProxyError{}
|
||||
}
|
||||
|
||||
func (svr *VirtualService) loopCheck() {
|
||||
<-svr.sshSvc.Exit()
|
||||
svr.pxy.Close()
|
||||
log.Info("virtual client service close")
|
||||
}
|
||||
|
||||
func (svr *VirtualService) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||
var pxyConf v1.ProxyConfigurer
|
||||
pxyConf, err = config.NewProxyConfigurerFromMsg(pxyMsg, &svr.serverCfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// User info
|
||||
userInfo := plugin.UserInfo{
|
||||
User: svr.loginMsg.User,
|
||||
Metas: svr.loginMsg.Metas,
|
||||
RunID: svr.runID,
|
||||
}
|
||||
|
||||
svr.pxy, err = proxy.NewProxy(svr.ctx, &proxy.Options{
|
||||
LoginMsg: svr.loginMsg,
|
||||
UserInfo: userInfo,
|
||||
Configurer: pxyConf,
|
||||
ResourceController: svr.rc,
|
||||
|
||||
GetWorkConnFn: svr.GetWorkConn,
|
||||
PoolCount: 10,
|
||||
|
||||
ServerCfg: &svr.serverCfg,
|
||||
})
|
||||
if err != nil {
|
||||
return remoteAddr, err
|
||||
}
|
||||
|
||||
remoteAddr, err = svr.pxy.Run()
|
||||
if err != nil {
|
||||
log.Warn("proxy run error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.Warn("proxy close")
|
||||
svr.pxy.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (svr *VirtualService) GetWorkConn() (workConn net.Conn, err error) {
|
||||
// tell ssh client open a new stream for work
|
||||
payload := forwardedTCPPayload{
|
||||
Addr: svr.serverCfg.BindAddr, // TODO refine
|
||||
Port: uint32(svr.pxyCfg.(*v1.TCPProxyConfig).RemotePort),
|
||||
}
|
||||
|
||||
channel, reqs, err := svr.sshSvc.SSHConn().OpenChannel(ChannelTypeServerOpenChannel, ssh.Marshal(payload))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open ssh channel error: %v", err)
|
||||
}
|
||||
go ssh.DiscardRequests(reqs)
|
||||
|
||||
workConn = frp_net.WrapReadWriteCloserToConn(channel, svr.sshSvc.tcpConn)
|
||||
return workConn, nil
|
||||
}
|
||||
Reference in New Issue
Block a user